diff --git a/finalaudit.md b/finalaudit.md index 5ef0adc1d..f5d9a14cc 100644 --- a/finalaudit.md +++ b/finalaudit.md @@ -8529,6 +8529,627 @@ This crosswalk covers the entire user-adopted target; it is an acceptance map, n The latest current-source regression checkpoint is maintained at the bottom of this file. Earlier dated checkpoints below retain their source identities and do not supersede this checkpoint merely because they appear later in the historical body. + +## Claude audit lanes β€” area audits merged to main β€” 2026-10-14 + +These lanes are area-by-area audits of the app. Each lane found candidate defects in one area, had every candidate it kept verified by two independent skeptics, fixed the confirmed ones in the repository with a test that fails before the fix and passes after it, had the fixes reviewed independently, and merged to main after full CI; the merged build is then verified live on www.bubaly.com/api/build-info. The counts below are taken from each PR's body as merged on GitHub; where a body gives no count the table says so, and review leftovers fixed in the same PR are shown in brackets beside the headline count. + +| Lane | PR | Merged (UTC) | Merge commit | Findings fixed in the repo | Owner items (SQL, product decisions, config) | Record | +|---|---|---|---|---|---|---| +| Operations and intelligence dashboards | [#1052](https://github.com/NewWorldVenture/Bubaly/pull/1052) | 2026-10-14 15:10 | `a40c040e8` | 19 (+6 follow-up fixes) | 16 | OPSD | +| Family coordination | [#1051](https://github.com/NewWorldVenture/Bubaly/pull/1051) | 2026-10-14 08:49 | `dd390e905` | 19 (+4 follow-up fixes) | 4 | COORD | +| AI features and agent runs | [#1050](https://github.com/NewWorldVenture/Bubaly/pull/1050) | 2026-10-14 08:16 | `f5580ca14` | 20 (+ follow-up fixes) | 2 | AIFR | +| Memories, notes and search | [#1049](https://github.com/NewWorldVenture/Bubaly/pull/1049) | 2026-10-14 03:20 | `c70dd852e` | 17 (+ follow-up fixes) | 8 | MEMO | +| Family accounts and notifications | [#1047](https://github.com/NewWorldVenture/Bubaly/pull/1047) | 2026-10-14 01:38 | `ebcef477a` | 20 (+ follow-up fixes) | 6 | ACCT | +| Money follow-ups | [#1044](https://github.com/NewWorldVenture/Bubaly/pull/1044) | 2026-10-13 21:19 | `9f55eff43` | 5 | 2 | MONEYF | +| Money | [#1043](https://github.com/NewWorldVenture/Bubaly/pull/1043) | 2026-10-13 20:12 | `70dbf2163` | 20 (+7 leftovers, 1 regression) | 8 | MONEY | +| Home operations | [#1042](https://github.com/NewWorldVenture/Bubaly/pull/1042) | 2026-10-13 17:23 | `63e12d464` | 19 (+2 leftovers) | 5 | HOMEOPS | +| Meals and food | [#1041](https://github.com/NewWorldVenture/Bubaly/pull/1041) | 2026-10-13 15:30 | `52d951358` | 19 (+4 leftovers) | 2 | MEALS | +| Kids and learning | [#1040](https://github.com/NewWorldVenture/Bubaly/pull/1040) | 2026-10-13 12:39 | `c660f2267` | 20 (+2 leftovers) | 6 | KIDS | +| Sensitive records | [#1039](https://github.com/NewWorldVenture/Bubaly/pull/1039) | 2026-10-13 08:04 | `2b5caab76` | 17 | 4 | SENS | +| Family-life modules | [#1031](https://github.com/NewWorldVenture/Bubaly/pull/1031) | 2026-10-13 04:47 | `a11915fcd` | 40 | 4 | FAMLIFE | +| Support batch 3: marketplace orders, AI plan gates, archived lists, crons, referral send limit, assistant chores | [#1037](https://github.com/NewWorldVenture/Bubaly/pull/1037) | 2026-10-13 04:09 | `4049f99a9` | 15 (six support branches) | 3 | SUPB3 | +| Support batch 2: Family Health medicine readers, removed child's login, provider-sync rotation | [#1036](https://github.com/NewWorldVenture/Bubaly/pull/1036) | 2026-10-13 03:21 | `3136816c0` | 3 | 1 | SUPB2 | +| AI authority follow-ups and public surfaces | [#1028](https://github.com/NewWorldVenture/Bubaly/pull/1028) | 2026-10-12 18:04 | `82efd76bd` | 23 (8 authority, 10 public-surface, 5 coordinator items) | 4 | AIAUTH | +| Support batch 1: referral credit once, deleted and archived data, child wallet ownership, server-action census | [#1022](https://github.com/NewWorldVenture/Bubaly/pull/1022) | 2026-10-12 16:44 | `06d89d3ff` | 11 (four support branches) | 0 (schema-change leftovers are deferred to #771, not listed in the body) | SUPB1 | +| AI tool registry | [#1016](https://github.com/NewWorldVenture/Bubaly/pull/1016) | 2026-10-12 11:28 | `b74e44b2f` | 26 | 3 | AITOOL | +| Guardian recovery | [#1013](https://github.com/NewWorldVenture/Bubaly/pull/1013) | 2026-10-11 09:47 | `d75264fbb` | 2 (a third item was refuted) | 0 | GREC | +| Audit follow-ups: Guardian; trips, school and sports; household modules | [#1004](https://github.com/NewWorldVenture/Bubaly/pull/1004) | 2026-10-11 07:57 | `c95b1180d` | 55, plus Guardian follow-ups in five areas (count not stated) | 8 | FOLLOW1 | +| Audit fixes, 16 areas (the body's table lists 17) | [#989](https://github.com/NewWorldVenture/Bubaly/pull/989) | 2026-10-10 20:05 | `209e5a48b` | 17 areas (finding count not stated) | 5 | AUD16 | +| Production schema fallbacks | [#982](https://github.com/NewWorldVenture/Bubaly/pull/982) | 2026-10-10 12:17 | `0039e3d23` | 5 areas | 3 | SCHFB | + +Status legend as in this file: πŸ›  FIXED + PASS = fixed in the repository with a test red before the fix; πŸ”„ IN PROGRESS = recorded for the owner (a migration, a product decision or a production setting), not applied by the lane. + +## Operations and intelligence dashboards β€” audit lane, PR #1052 β€” 2026-10-14 + +Result: **19 πŸ›  FIXED + PASS (plus 6 follow-up fixes from the review rounds), 16 πŸ”„ IN PROGRESS (owner items). Merged to main as `a40c040e8` on 2026-10-14 at 15:10 UTC after a green run of all 19 checks on `a47f5ef13`.** The lane audited the family operations and intelligence dashboards: Family Automation, the Family COO, Autonomous Family Management, Next Best Actions, Outcomes, Planning, Reasoning, Trust, the Knowledge Graph, Family Signals, the Operating Index, Family Stress, Activity, Bills due, Watchlist, Favorites, the Library, Career, Closet, Language, Scan, Customize and the App Store, with the cron jobs that compute what they show. Four lenses (authorization, record integrity, failure surfacing, external content) produced the candidates; the 20 most severe were each checked by two skeptical verifiers and 19 confirmed. Each fix has a test that drives the real page, component, action, route or service against the in-memory database; with the old source restored the fix-specific cases fail and every finding has at least one. Two review rounds on the audit fix and two follow-up rounds on what the reviewers found. Validation on the merged tree `a47f5ef13` (main plus this fix), run locally: `tsc --noEmit` clean; `eslint --no-ignore --max-warnings=0` on every changed TS/TSX file clean; `npm run i18n:gate` clean with no duplicate catalogue keys; all 235 source-scanning test files, 3,274 tests pass; the 60 test files that are changed or name a changed module, 925 tests pass in each of UTC, America/Los_Angeles and Asia/Tokyo; Playwright `bill-schedule-repair` and `finance-read-states`, 36 passed; each fix commit was validated by its fixer and re-run by an independent reviewer with negative controls. No merge commit yet; live verification not applicable until merged. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **OPSD-01** | N1 β€” Bills due: `/dashboard/due` lacked the gates Bills and Autopay have, so a guest could reach household money and no second factor was asked. | β€” | `/dashboard/due`; the Bills and Autopay gates; a guest's navigation. | **Fixed:** a guest is sent to the guest landing page; a signed-in member with a second factor enrolled is asked for it before household money is shown; the page is hidden from a guest's navigation, per the body. | +| **OPSD-02** | N6 β€” Budget drift: Family Intelligence and Reasoning showed the household's budget-drift amounts to every member, and anyone could dismiss or resolve the signal. | β€” | Family Intelligence; Reasoning. | **Fixed:** the amounts are shown to parents and adults only, and only they can dismiss or resolve the signal, per the body. | +| **OPSD-03** | N7 β€” Recommendations: any member could accept or dismiss a pending AI recommendation. | β€” | Recommendations. | **Fixed:** only a parent or adult can; everyone else sees the title and "Awaiting parent", per the body. | +| **OPSD-04** | N18 β€” Library content: the media proxy, Refresh and the nightly cron served or refreshed feeds and books regardless of who added them. | β€” | Media proxy; Refresh; nightly library cron. | **Fixed:** a feed or book is served only when its adder or subscriber is an active parent or adult; a book that names no adder is refused; the cron skips a feed no adult added, per the body. | +| **OPSD-05** | N2 β€” Network consent: a later tap could turn a withdrawn scope back on. | β€” | Consent toggles. | **Fixed:** each toggle writes from the row on screen and confirms the saved row, per the body. | +| **OPSD-06** | N4 β€” Run outcomes: runs still in flight were listed as done and failed runs were missing. | β€” | Recent runs. | **Fixed:** runs are selected by state: only completed runs get the green check, failed runs appear, in-flight runs are not listed as done, per the body. | +| **OPSD-07** | N8, N11 β€” Relative routines: a routine anchored to a trip, game or school event could fire for a stale anchor, and timestamp anchors were read on Greenwich's day. | β€” | Relative routines; `armPendingRoutines`. | **Fixed:** fires once, for the anchor as it is now; re-checks the anchor before filing; reads timestamp anchors on the family's day, per the body. | +| **OPSD-08** | N9, N12, N13 β€” Signals: a pattern that had stopped stayed active; stress windows were bucketed off the family's clock; routine adherence did not count the 28 days it claims. | β€” | Family Signals; Family Stress; routine adherence. | **Fixed:** a stopped pattern is retired; stress windows are bucketed on the family's clock; adherence counts the 28 days, per the body. | +| **OPSD-09** | N10 β€” Emergency sessions: the Trust console showed closed or expired sessions, without a per-session End, and activate and end were not compare-and-set. | β€” | Trust console; `activateEmergencyAction`. | **Fixed:** shows only open, unexpired sessions, each with its own End button; activate and end are compare-and-set, per the body. | +| **OPSD-10** | N14, N19 β€” Library ingest: an entry without a guid was keyed by its position, so refreshes duplicated it, and one bad episode failed the whole feed. | β€” | Library ingest. | **Fixed:** keyed by media URL, page link or a fingerprint, never its position; one bad episode (a repeated guid, a duration past int4) no longer fails the feed, per the body. | +| **OPSD-11** | N3 β€” The Decision Simulator said "fits cleanly" after a failed calendar or vacation read. | β€” | Decision Simulator. | **Fixed:** answers "Schedule unavailable", per the body. | +| **OPSD-12** | N15 β€” The Planning hub showed an empty state for a card whose read failed. | β€” | Planning hub. | **Fixed:** the card says it could not load, per the body. | +| **OPSD-13** | N16 β€” The Knowledge Graph read at most 1,000 rows. | β€” | Knowledge Graph. | **Fixed:** reads the whole graph past 1,000 rows and shows an error state past its ceiling, per the body. | +| **OPSD-14** | N17 β€” The Trust ledger showed "No activity yet" after a failed audit-log read. | β€” | Trust ledger. | **Fixed:** shows a retryable error, per the body. | +| **OPSD-15** | N5 β€” Feed parser: a hostile 4 MB feed blocked the event loop for minutes, and a feed that killed a run was fetched first again. | β€” | Feed parser; nightly run. | **Fixed:** parses in under 1.5 s; a feed is claimed before it is fetched, so one that kills a run is queued last the next night, per the body. | +| **OPSD-16** | Follow-up β€” The household's daily Operating Index snapshot and its reasoning snapshot could be overwritten by a narrowed reader's visit. | β€” | Operating Index and reasoning snapshots. | **Fixed (follow-up commit):** written only from a parent's or adult's visit; everyone else is shown their own view and writes nothing, per the body. | +| **OPSD-17** | Follow-up β€” The Reasoning page for non-managers dropped money items by matching words, so a child lost ordinary calendar items such as "Bill's recital". | β€” | Reasoning page. | **Fixed (follow-up commit):** drops money items by where they come from (the financial dimension, document renewals, links to money pages), per the body. | +| **OPSD-18** | Follow-up β€” Insight of the day: a non-manager's visit could rewrite the household's rows, and a sensitive document's title could reach a child. | β€” | Insight of the day. | **Fixed (follow-up commit):** a parent's visit writes the household's rows; anyone else's visit never rewrites them and is shown only their own candidates, per the body. | +| **OPSD-19** | Follow-up β€” Review P3s: the relative routine re-check, the run outcome filter, recommendation bodies on Home and Needs-you, and the reasoning page's money counts. | β€” | The body refers to the commits without further detail. | **Fixed (follow-up commits):** per the body. | +| **OPSD-20** | Follow-up β€” The Operating Index page, Briefing and Command Center showed a member who does not read household money the financial moves, document renewals, money links, the financial summary and a "since yesterday" recap or trend built from the household's row. | β€” | Operating Index page; Briefing; Command Center; Calm. | **Fixed (follow-up commit):** such a member gets an index without those items, no financial summary and no recap or trend from the household's row; headline counts match what is shown; Calm filters the same way; parents and adults see everything as before, per the body. | +| **OPSD-21** | Follow-up β€” The assistant's proactive slice applied the money-word filter to everything, so a child's assistant lost ordinary calendar items. | β€” | Proactive context slice. | **Fixed (follow-up commit):** composes its report by source, as the reasoning page does, and applies the money-word filter only to Autopilot and approval items; the word rule also matches plurals and the financ- stem where it still applies, per the body. | +| **OPSD-O01** | N18 (SQL) β€” The in-app check reads `created_by`, which RLS lets any member set or leave empty; an empty `created_by` is still honoured because `tests/the-family-library-keeps-every-book-and-asks-a-grown-up.test.ts` (S20) requires a feed whose subscriber was deleted to keep refreshing, so a child who writes over PostgREST and leaves it empty, or names a parent's user id, is not stopped. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: a BEFORE INSERT/UPDATE trigger on `library_feeds.feed_url` and `library_items.media_url` that refuses the write unless `family_role(family_id)` is 'parent' or 'adult' or `auth.uid()` is null, and pins `created_by` to `auth.uid()`. | +| **OPSD-O02** | N8 (SQL) β€” Case (c), a new earlier anchor (a trip added before the armed one), is still missed; nothing re-arms a relative routine when an anchor row is inserted or moved earlier, and a code-only pass over every armed rule on each tick would be unbounded. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: a trigger on `vacations`, `calendar_events`, `school_events`, `sports_events` and `bills` that sets `next_run_at` to null on the family's relative `family_automation_rules` for that anchor so `armPendingRoutines` re-arms them; plus an anchor-occurrence column on `routine_runs` (anchor row id and day) with a unique index so the same occurrence is never filed twice. | +| **OPSD-O03** | N10 (SQL) β€” `activateEmergencyAction` refuses beside an open session with a read followed by an insert, so two managers activating at the same instant can both get in. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: a partial unique index on `emergency_sessions(family_id)` where `ended_at` is null, accounting for expired-but-unended rows (for example by ending them first). | +| **OPSD-O04** | N18 (SQL migration, detailed) β€” Until a trigger exists, the media proxy, Refresh and the nightly cron trust `created_by`, which RLS (`is_family_member`, 0284) does not tie to the writer; code cannot close five member writes over PostgREST: a feed or book whose `created_by` names a parent's id; a feed inserted with `created_by` left out (still refreshed and played, since a feed with no subscriber must keep refreshing, S20); an episode inserted with `feed_id` set to an adult's feed and any `media_url`; changing `media_url` in place on an adult's book or episode; the same in-place change of `feed_url` on an adult's feed; code now refuses only a hand-added book (`feed_id` null) with no `created_by`. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: a BEFORE INSERT OR UPDATE trigger on `library_items` (`media_url`, `feed_id`, `created_by`) and `library_feeds` (`feed_url`, `created_by`) refusing the write when `auth.uid()` is not null and the writer is not an active parent or adult of the row's family, setting `created_by` to `auth.uid()` on insert and keeping it on update; caveat: `refreshFeedAction` runs `ingestFeed` on the presser's own client, so either exempt the ingest upsert or move Refresh to ingest on the service client after its curator check. | +| **OPSD-O05** | N9-R1 (SQL migration) β€” 01420 still gives `family_signals` select, insert, update and delete to `is_family_member(family_id)`; the app paths are closed, but over PostgREST a child can still DELETE the parents' `budget_drift` row, PATCH its status, or SELECT its title and evidence (spent and limit). | β€” | finalaudit.md DB-TBL-138 lists the per-role check for `family_signals` as Remaining, per the body. | πŸ”„ IN PROGRESS β€” owner action: narrow UPDATE and DELETE (and SELECT, if the N6 read rule should hold at the database) for `kind = 'budget_drift'` to `can_manage_family(family_id)`. | +| **OPSD-O06** | N9-S1, the database half (SQL migration) β€” 0125 (`family_operating_index` insert and update) and 0149 (`reasoning_snapshots`) still allow any member to write the household's day directly over PostgREST. | β€” | The app no longer upserts these rows from a non-manager's client, per the body. | πŸ”„ IN PROGRESS β€” owner action: narrow those writes to `can_manage_family(family_id)` or to the service role. | +| **OPSD-O07** | SQL β€” `daily_insights` is unique on `(family_id, as_of_date, kind)` (0141), so when a non-manager dismisses their own insight of a kind the household's row for that kind is marked dismissed and the parents lose it for the day; this predates the change. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: key rows per viewer (for example `member_id` in the unique key with nulls not distinct) or restrict which updates a non-manager may make; needs a migration. | +| **OPSD-O08** | N9-S2-AT-REST (SQL) β€” `member_id`-null rows of the document, renewal and autopilot kinds in `daily_insights` are selectable by every member. | β€” | The body gives an example policy using `is_family_member` and `can_manage_family`; the `ignoreDuplicates` insert from a child's visit would then conflict silently with the hidden household row, so that child's candidate of that kind would not be shown, which the body calls acceptable degradation. | πŸ”„ IN PROGRESS β€” owner action: narrow 0141's `daily_insights_select` so those rows are selectable only by managers. | +| **OPSD-O09** | N9-S2-AT-REST, reviewer's correction to the proposed policy. | β€” | Corrected policy per the body: `create policy daily_insights_select on public.daily_insights for select using (public.is_family_member(family_id) and (kind not in ('document','renewal','autopilot') or public.can_manage_family(family_id) or (member_id is not null and public.is_self_member(member_id))))`; `is_self_member` is in `supabase/migrations`. | πŸ”„ IN PROGRESS β€” owner action: apply the corrected policy, or take the finding's code-only route of storing only kind and impact for those kinds. | +| **OPSD-O10** | N9 (owner decision or SQL) β€” 0142's status check has no 'resolved' value, so stale active signals are deleted, not resolved, and the decision-compression metric (`lib/metric/strategy-server.ts`) no longer counts a retired row; existing decided rows with the old key format `budget:` remain. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner decision: widen the status check to add 'resolved' to keep the history; the old-format rows are harmless and can be cleaned up with SQL. | +| **OPSD-O11** | N14 (one-off data cleanup) β€” Existing `library_items` rows keyed `title#index` remain; the first refresh after deploy files those entries once more under their new keys (page link or fingerprint). | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: after one refresh, delete feed items whose guid matches `#[0-9]+$`. | +| **OPSD-O12** | N18 behaviour change to confirm β€” a hand-added book whose adder's account was later deleted (0284 sets `created_by` to null) no longer plays through `/library/media`. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner decision: confirm; a parent has to add it again. | +| **OPSD-O13** | N9-S1 (owner decision, code change if wanted) β€” snapshots are kept only from a manager's own client, so a day on which no parent or adult opens the Operating Index, briefing, Command Center or reasoning page, and no manager uses the assistant, gets no row and the next day's trend and recap skip it; even a manager's snapshot can depend on who looked, since `unreadThreads` reads `family_messages`, which 0475 narrows per conversation roster. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner decision: if one household snapshot regardless of per-viewer RLS is wanted, write it from the model-refresh cron on the service client (a code change not made here). | +| **OPSD-O14** | N8 (recorded, carried over from round 1, unchanged) β€” an anchor row added earlier than the armed `next_run_at` is not seen until the rule fires, because nothing re-arms a rule when an anchor row is inserted; `routine_runs` is keyed on `(rule_id, due_at)`, not on the anchor occurrence. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” recorded for the owner (see OPSD-O02 for the SQL). | +| **OPSD-O15** | Recorded, not changed β€” the readiness dimension summary still counts expiring documents ('N docs expiring'); for a non-manager the count covers only the documents their own client can read, because 0266's `documents_select` already hides sensitive documents; the renew-docs move itself is withheld. | β€” | Outside the listed fixes, per the body. | πŸ”„ IN PROGRESS β€” recorded for the owner; no action named. | +| **OPSD-O16** | Recorded, not changed β€” on `/dashboard/calm` a finance Autopilot card ('not-guests' audience in `lib/autopilot/audience.ts`) is still listed to a child by the card audience rule, separate from the operating index rule fixed here. | β€” | Existing behaviour, per the body. | πŸ”„ IN PROGRESS β€” recorded for the owner; no action named. | + +## Family coordination β€” audit lane, PR #1051 β€” 2026-10-14 + +Result: **19 πŸ›  FIXED + PASS (plus 4 follow-up fixes from the review round), 4 πŸ”„ IN PROGRESS (owner items).** The lane audited the locator and location history, contacts, connections, the grandparent portal, todos, goals, conflicts, sign-ups, workload, behaviour, sleep, focus, calm, family stress, readiness and prep plans; four lenses (authorization, record integrity, failure surfacing, external content) produced the candidates, the 20 most severe were each checked by two skeptical verifiers, 19 were confirmed and 1 refuted. Each fix has a test that drives the real route, page, component, action or service; with the old source restored 60 of the new cases fail and every finding has at least one. Validated on `a04164b4e` (main `f5580ca14` plus this PR): tsc, eslint and the i18n gate clean; 235 source-scanning files (3,273 tests) and 133 touched files (2,537 tests) pass in UTC, America/Los_Angeles and Asia/Tokyo; e2e `inbox-message-ownership` passes (12 tests). Merged as `dd390e905`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **COORD-01** | N1, N2 β€” Behaviour notes: the behaviour insight and `/api/behavior/insight` gave a teen, child or guest a model summary built from notes other people wrote. | β€” | `/api/behavior/insight`, the behaviour insight and the coaching route; the Behaviour page's role rule is the reference. | **Fixed:** the insight reads only the notes the caller wrote themselves; the coaching route answers only parents, adults and caregivers, the roles the Behaviour page shows every note to, per the body. | +| **COORD-02** | N4 β€” Sleep coach: a non-manager's sleep coach read other members' nights. | β€” | Sleep coach read path. | **Fixed:** reads only their own nights and the entries they logged, per the body. | +| **COORD-03** | N5 β€” Contact timeline: a guest's contact timeline showed the household inbox's messages, and the reconnect draft included them. | β€” | Contact timeline; reconnect draft. | **Fixed:** both leave the inbox messages out for a guest, per the body. | +| **COORD-04** | N6 β€” Family Map: a child's or teen's map read every member's arrival and departure history. | β€” | Family Map location-history query. | **Fixed:** reads only their own history, narrowed in the query before its limit, per the body. | +| **COORD-05** | N16 β€” Reconnect draft prompt: the contact history and the contact's fields reached the prompt unfenced. | β€” | Reconnect draft prompt; the data-not-instructions rule. | **Fixed:** both are fenced as untrusted data, per the body. | +| **COORD-06** | N7 β€” Turning location sharing back on was treated as an "arrived" event and sent an urgent place alert. | β€” | Location sharing and place alerts. | **Fixed:** re-enabling sharing is a baseline, not an arrival, and sends no urgent alert, per the body. | +| **COORD-07** | N8, N10 β€” Prep plans: dismissing a birthday or document plan hid every later year; readiness counted steps of inactive plans; plans whose date had passed stayed. | β€” | Prep plan generation and readiness. | **Fixed:** plans belong to their occurrence; readiness counts only overdue steps of active plans; past-dated plans are retired, per the body. | +| **COORD-08** | N9, N17 β€” Focus Mode finished a chore and completed a todo its own way, bypassing approval, next week's assignment, `completed_at` and the activity row. | β€” | Focus Mode; the Chores page and Todos page completion paths. | **Fixed:** a chore is finished the way the Chores page finishes it (respects approval, creates next week's assignment); a todo is completed the way the Todos page completes it (`completed_at` and its activity row), per the body. | +| **COORD-09** | N11 β€” Conflict quick fix moved a whole repeating series and acted on stale data. | β€” | Conflicts quick fix. | **Fixed:** a quick fix no longer moves a whole series and is refused when the event was rescheduled after the page was shown, per the body. | +| **COORD-10** | N18, N19 β€” Sleep logs were entered and dated off the family's clock; a workload snapshot was not the family's ISO week and was not confirmed. | β€” | Sleep log entry; workload snapshot save. | **Fixed:** sleep logs use the family's clock; a workload snapshot is the family's ISO week, saved by a manager and confirmed, per the body. | +| **COORD-11** | N3 β€” The Conflicts page checked raw rows only and said "No schedule conflicts" when it could not check everything. | β€” | Conflicts page fortnight read. | **Fixed:** checks every occurrence of the fortnight, including repeating events and events already under way, and says when it could not check everything, per the body. | +| **COORD-12** | N12, N13 β€” The contacts, sign-ups and behaviour insights answered from failed reads; the sign-ups advisor read the wrong sign-ups. | β€” | Insights; sign-ups advisor. | **Fixed:** the insights answer "could not load" when a read fails; the advisor reads the open, soonest sign-ups on the family's day, per the body. | +| **COORD-13** | N14, N15 β€” Capped reads: the contact book stopped at 1,000 contacts; the assistant context and the kitchen speaker treated a capped read as the total. | β€” | Contact book read; assistant context; kitchen speaker. | **Fixed:** the contact book reads past 1,000; the other two say "showing X of N", per the body. | +| **COORD-14** | Follow-up β€” Command Center said "Everything looks handled" on a capped or failed read and checked conflicts on raw rows. | β€” | Command Center week read; the body says its occurrence-key test fails on the follow-up's own code and passes with the fix. | **Fixed (follow-up commit):** checks every occurrence of the week, repeating and under-way events included; each occurrence is its own row so only the clashing one is badged; no "handled" claim on a capped or failed read, per the body. | +| **COORD-15** | Follow-up β€” The Inbox contact picker stopped at 1,000 contacts. | β€” | Inbox contact picker. | **Fixed (follow-up commit):** reads past 1,000, per the body. | +| **COORD-16** | Follow-up β€” A non-manager's Sleep page read other members' nights and entries. | β€” | Sleep page. | **Fixed (follow-up commit):** reads only their own, narrowed before its limits, per the body. | +| **COORD-17** | Merge repair β€” the reconnect draft and two tests had to be reconciled with the Free-plan allowance check #1050 added. | β€” | Reconnect draft; contact timeline test; coordination insight test. | **Fixed (follow-up commit):** keeps this PR's fencing and #1050's allowance check; the tests admit the allowance as the other insight tests do, per the body. | +| **COORD-O01** | Writes to `workload_snapshots` are not restricted to managers at the database. | β€” | The app now refuses non-managers, but PostgREST does not; needs RLS, per the body. | πŸ”„ IN PROGRESS β€” owner action: restrict writes to `workload_snapshots` to managers in RLS. | +| **COORD-O02** | `family_communications` is readable by guests at the database. | β€” | Needs an RLS rule consistent with the guest rule for the inbox, per the body. | πŸ”„ IN PROGRESS β€” owner action: withhold `family_communications` from guests in RLS. | +| **COORD-O03** | Sleep, behaviour and location history are narrowed only in the app. | β€” | Held migrations 0506, 0510 and 0516 carry the database half, per the body. | πŸ”„ IN PROGRESS β€” owner action: release 0506, 0510 and 0516. | +| **COORD-O04** | Product decision: should a member's very first location share inside an armed geofence be an "arrived" event? | β€” | Two existing tests expect it to be, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + +Left as is, per the body: P3, correcting an upcoming birthday or document expiry date reopens a dismissed plan and clears its ticks (two attempted fixes each brought a P2 regression for a birthday corrected across today and were taken out); P3, the Operating Index, the Agents page and the autopilot scan still check conflicts on raw calendar rows, so a clash with a repeating event created before the window is missed there. + +## AI features and agent runs β€” audit lane, PR #1050 β€” 2026-10-14 + +Result: **20 πŸ›  FIXED + PASS (plus follow-up fixes from four review rounds), 2 πŸ”„ IN PROGRESS (owner items).** The lane audited the AI features members call directly (assist, chef, flyer, gift, habits, health, invest, journal, relationship, requests, savings, schedule, trip, voice, wallet, weekly briefing and import) and the run layer behind them (planner, executor and its controls, routine cron, autonomy); four lenses produced the candidates, the 20 most severe were each checked by two skeptical verifiers and all 20 confirmed (N4 is the same defect as N2). Each fix has a test, driven through the real route, action, service or component, that fails on the base; the follow-up commits were reviewed again independently and no P0, P1 or P2 is open. Validated on `0778a6d89` (main `c70dd852e` plus this PR): tsc, eslint and the i18n gate clean; 235 source-scanning files (3,273 tests); 240 touched files (3,790 tests) in UTC, America/Los_Angeles and Asia/Tokyo; e2e 129 pass across `assistant-conversation-lifecycle`, `inbox-message-ownership`, `quick-capture-task` and `weekly-meal-planner`; the full unit suite runs clean on CI's Node 24. Merged as `f5580ca14`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **AIFR-01** | N1, N10 β€” Editing or resuming a run in flight: an edited step could run twice (old args, then new) or be lost under a "completed". | β€” | Run editor; executor worker and its lease. | **Fixed:** an edit is refused while a run executes or holds a live lease; the worker keeps the lease token it claimed with, stops when the lease changes hands, releases only its own lease on pause, and reloads the steps when the plan changes under it, per the body. | +| **AIFR-02** | N11 β€” Editing a step disturbed the other steps' status, results and approvals. | β€” | Plan edit path. | **Fixed:** the other steps keep their status, results and approvals; pending approvals of superseded steps are cancelled; a paused run stays paused after an edit, per the body. | +| **AIFR-03** | N3, N9 β€” A planner the intake gave up on could still launch a run nobody was watching, or file a recommendation after the intake closed the request. | β€” | Planner outcome writes. | **Fixed:** each planning attempt has its own mark and every planner outcome write is fenced on it, per the body. | +| **AIFR-04** | N2, N4 β€” A routine filed a 'queued' run with no plan; nothing claimed it, the request and run stayed "in progress" forever, and the cron counted it as filed. | β€” | Routine cron. | **Fixed:** the cron plans the run first, kicks only a planned run, and a failed plan marks the request and the run failed, per the body. | +| **AIFR-05** | N8 β€” A lapsed or Free family's runs kept going. | β€” | ai-runs cron, every kick, approvals and resume. | **Fixed:** entitlement is checked on every way into a run; runs are put back a day out without spending attempts in the kick path; routines of unentitled families are skipped, per the body. | +| **AIFR-06** | N12 β€” "Handle it" could file more than one run per message. | β€” | `tests/handle-it-files-one-run-per-message.test.ts`; main already carried the same claim from #1049. | **Fixed:** the PR keeps main's implementation and this audit's test passes against it, per the body. | +| **AIFR-07** | N13 β€” "No calendar conflicts" counted events instead of finding clashes, and meal-plan checks counted the wrong dates. | β€” | The calendar's `findConflicts`; meal-plan check. | **Fixed:** uses `findConflicts`; meal-plan checks count only the planned dates, per the body. | +| **AIFR-08** | N5, N14, N20 β€” Weekly briefing: a failed household read gave an empty brief, an unusable model reply gave a made-up brief, and the route checked a bare plan level. | β€” | Weekly briefing route. | **Fixed:** a failed read answers 503, an unusable reply 502, and the route answers to its page's gate, per the body. | +| **AIFR-09** | N15 β€” The savings coach showed errors and malformed replies as "AI suggestions". | β€” | Savings coach card. | **Fixed:** they are no longer shown as suggestions and the card says when the numbers are its own, per the body. | +| **AIFR-10** | N16 β€” Find time showed "No shared openings" after a failed search. | β€” | Find time. | **Fixed:** shows an error with "Try again", per the body. | +| **AIFR-11** | N6 β€” A `/dashboard/assistant?q=…` link sent its text, which could ask the write-capable assistant to act. | β€” | Assistant page query handling. | **Fixed:** the link only fills the draft; in-app questions use a one-time hand-off, per the body. | +| **AIFR-12** | N19 β€” `/api/ai/assist` was a general model proxy. | β€” | `/api/ai/assist`. | **Fixed:** requires a purpose (`concierge` or `inbox_reply`); the server owns the prompts and token budgets; input is bounded; an inbox reply reads its message by id and family, fenced as untrusted, per the body. | +| **AIFR-13** | N7 β€” The Relationship Helper's daily family cap held only for parents. | β€” | Relationship Helper. | **Fixed:** the cap holds for every member, per the body. | +| **AIFR-14** | N17, N18 β€” Voice transcription and speech, and the module helpers, were not metered. | β€” | Voice routes; the Habits, Journal and Notes helpers, meal planner, nutrition and every insight kind. | **Fixed:** voice is recorded as AI requests, capped at 2 MB, and the recorder stops at 60 s; the helpers check the Free plan's 10 a month, per the body. | +| **AIFR-15** | Follow-up β€” "Try again" on a run. | β€” | Run controls. | **Fixed (follow-up commit):** on a paused run it requeues the step and its blocked dependents and keeps the run paused until Resume; refused while the run executes or holds a live lease; kicks only a run that is now 'ready', per the body. | +| **AIFR-16** | Follow-up β€” A single allowance check (`assertAIAllowance`) for the Free plan. | β€” | Recipe suggest and transform; contact reconnect draft; paperwork reply draft; marketplace assistant; Relationship Helper; AI review of a kid's chore proof. | **Fixed (follow-up commit):** checks the lock, the monthly allowance and this month's count, fails closed when the count cannot be read; the marketplace assistant falls back to its grounded reply and the chore review to parent review, per the body. | +| **AIFR-17** | Follow-up β€” Helpers rode on the "AI Assistant" switch, so switching it Off or raising its tier switched them off. | β€” | Habits, Journal, Notes, voice transcription, meal planner, nutrition and every insight kind. | **Fixed (follow-up commit):** each checks the allowance on its own and answers to its own page's gate, per the body. | +| **AIFR-18** | Follow-up β€” Smaller fixes. | β€” | ai-runs cron daily requeue; late planner; savings reply; phone recorder. | **Fixed (follow-up commit):** the requeue no longer spends a run's attempt; a late planner's run is created where nothing can claim it; a summary-only savings reply no longer labels the computed list as AI; the phone's recorder stops and sends at 60 s, per the body. | +| **AIFR-19** | Merge repairs. | β€” | Two insight tests; the Handle-it refusal key; the oversized-recording test. | **Fixed (follow-up commit):** the insight tests admit the allowance; the refusal key is said once in main's wording; the oversized-recording test streams its upload so Node 24 raises no unhandled rejection, per the body. | +| **AIFR-O01** | Owner decision: should the module helpers count toward Free's 10 AI requests a month? | β€” | They already write `ai_requests` rows the monthly count reads; the PR refuses them past the allowance as the finding was written, per the body. | πŸ”„ IN PROGRESS β€” owner decision; removing the checks together reverses it. | +| **AIFR-O02** | Without held migration 0493 (`count_family_ai_requests_month`) the allowance counts `ai_requests` under the caller's RLS, so a child's own client may count fewer of the household's rows. | β€” | The database-side count needs the migration, per the body. | πŸ”„ IN PROGRESS β€” owner action: apply 0493. | + +Left as is, per the body: P3, "Try again" writes its step requeue without a compare-and-set fence on the paused path; P3, a few helpers behind Basic or Plus pages do not check the allowance (matters only if an admin lowers those pages to Free). The body's "Owner action" list says none is needed for these fixes. + +## Memories, notes and search β€” audit lane, PR #1049 β€” 2026-10-14 + +Result: **17 πŸ›  FIXED + PASS (plus follow-up fixes from the review rounds), 8 πŸ”„ IN PROGRESS (owner items).** The lane audited the family's memories, notes and shared knowledge: photos, trip memories, the family tree, Family Memory, Life & Milestones, notes, the inbox and Contact Center, the social feed, voting and decisions. Four lenses produced the candidates; the 20 most severe were each checked by two skeptical verifiers, 17 confirmed, 2 disputed and 1 refuted. Each fix has a test that drives the real page, action, route or component; with the old source restored 54 of the 68 new cases failed and every finding had at least one. Two review rounds closed 15 findings and 3 further items; follow-up commits closed one P1, one P2 and three P3s, were reviewed again, and that review's one P3 is closed too. Validated on `08b5dea4c` (main `ebcef477a` plus this PR): tsc and the i18n gate clean; eslint shows 6 `jsx-a11y` warnings in `components/modules/notes-module.tsx` that main also has from the recorded lint backlog; 234 source-scanning files (3,265 tests); 164 touched files (2,674 tests) in UTC, America/Los_Angeles and Asia/Tokyo; e2e 60 pass across `decisions-choice-readback`, `inbox-message-ownership`, `notes-modal-completion`, `notes-save-rejection` and `school-desk-read-boundaries`. Merged as `c70dd852e`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **MEMO-01** | N1 β€” A guest could read the household inbox on the Contact Center. | β€” | Contact Center page; `/dashboard/contact-center` and `/dashboard/front-desk`. | **Fixed:** the page refuses a guest, reads the inbox with the caller's own client (so reserved 0509 applies there once applied), and both routes are on the guest-withheld list, per the body. | +| **MEMO-02** | N2 β€” Any member could mark read or archive an inbox message. | β€” | Inbox message actions and buttons. | **Fixed:** only a parent or adult on Family+ can, and the buttons show only for them, per the body. | +| **MEMO-03** | N3, N6 β€” A trip-memory delete was offered to non-managers, and a refused delete destroyed the photo first. | β€” | Trip memory delete. | **Fixed:** only a manager is offered the delete; a refused delete no longer destroys the photo first, per the body. | +| **MEMO-04** | N7 β€” Family Memory and Life & Milestones showed sensitive facts to children, teens, caregivers and guests. | β€” | `factsVisibleTo`; the learned list and the Coming up section. | **Fixed:** sensitive facts are hidden from those roles, per the body. | +| **MEMO-05** | N8, N17 β€” "Handle it" could be pressed by any role, and twice. | β€” | Handle it action. | **Fixed:** only a parent or adult; the message is claimed once so two presses file one request; a failed intake gives the claim back, per the body. | +| **MEMO-06** | N4 β€” The family tree accepted a parent that made someone their own ancestor, and an existing loop vanished from the drawing. | β€” | Family tree. | **Fixed:** such a parent is refused and an existing loop is drawn, per the body. | +| **MEMO-07** | N9 β€” A note saved over a partner's newer edit silently won. | β€” | Notes editor. | **Fixed:** the save is refused and the editor keeps your text, per the body. | +| **MEMO-08** | N10, N12 β€” A vote on a closed poll was accepted; the poll deadline was shown off the family's clock. | β€” | Voting. | **Fixed:** closed-poll votes are refused; the deadline is read and shown on the family's clock, per the body. | +| **MEMO-09** | N11 β€” A logged call kept UTC wall time. | β€” | Call log. | **Fixed:** keeps the family's time, per the body. | +| **MEMO-10** | N13 β€” Poll tallies, voter counts and decision options were cut at 1,000 rows. | β€” | Polls and decisions reads. | **Fixed:** read to the end, per the body. | +| **MEMO-11** | N15 β€” Create Memory did not say how many photos it kept. | β€” | Create Memory. | **Fixed:** says how many it kept and offers a retry for the ones it missed, per the body. | +| **MEMO-12** | N5 β€” The Photos page past 1,000 rows lost photos; totals, counts and search were built on a capped read. | β€” | Photos page. | **Fixed:** Load more reads one page at a time; "Showing N of M", the album counts, the covers and the favourites badge are each read on their own; search runs in the database and the box stays mounted while results load, per the body. | +| **MEMO-13** | N14 β€” The notes, photos and memories insights answered from failed reads. | β€” | Insights. | **Fixed:** answer "could not load" when a read fails and give real totals, per the body. | +| **MEMO-14** | N16 β€” A Social Feed that could not be read showed "Your feed is empty". | β€” | Social Feed. | **Fixed:** shows an error with a retry, per the body. | +| **MEMO-15** | Follow-up β€” The photos grid was faded and still reachable while a search loaded. | β€” | `tests/informative-text-is-not-faded.test.ts` was red on the faded grid. | **Fixed (follow-up commit):** drawn at full strength and made `inert`; "Searching…" is announced in a live region, per the body. | +| **MEMO-16** | Follow-up β€” Load more and tag search. | β€” | Photos hook; tag search. | **Fixed (follow-up commit):** Load more counts the hook's own realtime and reconnect re-reads and reads the grid a page longer rather than appending over one; tag search matches the term as typed and lower-cased, so "Grandpa" finds "grandpa", per the body. | +| **MEMO-17** | Follow-up β€” The Knowledge Graph twin projection wrote sensitive preferences ("Emma allergies: peanuts"). | β€” | Twin projection; the preference read's 500-row cap. | **Fixed (follow-up commit):** writes only the preferences every member may read; a node an earlier run wrote for one is pruned even when the read hits its cap; parents still read the fact on Family Memory, per the body. | +| **MEMO-18** | Follow-up β€” Needs you and the School & Sports desk read the household inbox for a guest. | β€” | Needs you; School & Sports desk. | **Fixed (follow-up commit):** neither reads nor shows it for a guest, per the body. | +| **MEMO-19** | Test harness β€” `fetchedQueryModule` did not drop a superseded read as the real `useRealtimeQuery` does. | β€” | Test harness used by 49 tests. | **Fixed (follow-up commit):** it drops a superseded read; all 49 tests pass and no assertion was weakened, per the body. | +| **MEMO-O01** | A direct browser insert can still vote on a closed poll. | β€” | Needs a database guard, per the body. | πŸ”„ IN PROGRESS β€” owner action: a database guard so a closed poll takes no votes. | +| **MEMO-O02** | A family-tree loop is refused only in the app. | β€” | Optional, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional): a trigger refusing a family-tree loop. | +| **MEMO-O03** | The documents bucket's delete policy for `{familyId}/trip-memories/*` is not manager-only. | β€” | Storage policy, per the body. | πŸ”„ IN PROGRESS β€” owner action: narrow the delete policy to managers. | +| **MEMO-O04** | Guests are not refused inbox rows at the database. | β€” | Reserved migration 0509, per the body. | πŸ”„ IN PROGRESS β€” owner action: apply 0509. | +| **MEMO-O05** | No RLS rule for sensitive facts. | β€” | Already recorded in 0385, per the body. | πŸ”„ IN PROGRESS β€” owner action: an RLS rule for sensitive facts. | +| **MEMO-O06** | A Handle-it claim stranded by a crash cannot be released. | β€” | Needs columns, per the body. | πŸ”„ IN PROGRESS β€” owner action: `family_inbox_messages.request_id` and `handled_at`. | +| **MEMO-O07** | A capitalised or partial tag is matched only in the app. | β€” | Needs a generated lower-cased tags column or a search RPC, per the body. | πŸ”„ IN PROGRESS β€” owner action: a database-side tag match. | +| **MEMO-O08** | The graph has no per-row audience, so parents lose their sensitive preference nodes. | β€” | `graph_entities`, per the body. | πŸ”„ IN PROGRESS β€” owner action: a per-row audience on `graph_entities`. | + +## Family accounts and notifications β€” audit lane, PR #1047 β€” 2026-10-14 + +Result: **20 πŸ›  FIXED + PASS (plus follow-up fixes from four review rounds), 6 πŸ”„ IN PROGRESS (owner items).** The lane audited settings, profile, family access, setup and onboarding, invites, notifications and their digests, push, announcements, celebrations and moments. Four lenses (authorization, record integrity, failure surfacing, external delivery) produced 27 candidates; the 20 most severe were each checked by two skeptical verifiers and all 20 confirmed. Each fix has a test, driven through the real action, route, component or service, that fails on the base; the fixer counted 81 failing cases across 14 new test files. Two review rounds closed 19; the last P2 (push dispatch) and the leftovers were closed by follow-up commits reviewed in two more rounds, which left no P0, P1 or P2. Validated on `fecd36a73` (main `06ac6e3b5` merged in): tsc, eslint and the i18n gate clean; 234 source-scanning files (3,263 tests); 241 touched files (3,752 tests) in UTC, America/Los_Angeles and Asia/Tokyo (25 skips are in one pre-existing environment-gated file); `tests/e2e/notification-email-preference.spec.ts` 14 pass. Merged as `ebcef477a`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **ACCT-01** | A1 β€” A removed member could rejoin through an older pending invite, and managers had no way to revoke one. | β€” | Removal; join; Settings > Members. | **Fixed:** removal revokes the family's pending invites to that person; joining expires the joiner's other invites to the same address; Settings > Members lists pending invites, each with a Revoke button, per the body. | +| **ACCT-02** | A7, A12 β€” Member edits went several ways, could give a kid-PIN login a manager role, and could overwrite a removed or concurrently changed member. | β€” | Member edit server action; trust ledger. | **Fixed:** one manager-only server action that refuses a manager role for a kid-PIN login, re-reads the row, refuses a removed or meanwhile-changed member, writes only the changed columns, and sends role changes to the trust ledger, per the body. | +| **ACCT-03** | A8 β€” Any member could save the household questionnaire. | β€” | Questionnaire save. | **Fixed:** manager only, and only for their own active family, per the body. | +| **ACCT-04** | A6 β€” Geofence arrive and leave alerts went to members who may not read location history. | β€” | Geofence alerts. | **Fixed:** only to members who may read location history, per the body. | +| **ACCT-05** | A18 β€” `/join` accepted an invite on page load. | β€” | `/join`. | **Fixed:** shows the family and role and accepts on a click, per the body. | +| **ACCT-06** | A3 β€” A failed medication or dose read re-sent reminders or announced doses already given. | β€” | Medication reminder tick. | **Fixed:** that tick's medication reminders are skipped, per the body. | +| **ACCT-07** | A9 β€” A dismissed notification was re-created and re-sent by the generator. | β€” | Notification generator. | **Fixed:** the dismissed notification is kept, marked and settled, per the body. | +| **ACCT-08** | A15 β€” Sources the generator could not read were reported as "You're all caught up". | β€” | Notification crons; Scan. | **Fixed:** the crons answer 502 and Scan says some updates could not be checked, per the body. | +| **ACCT-09** | A19 β€” Family-wide notices were one row, so one member's "mark all read" cleared everyone's. | β€” | Notices. | **Fixed:** written once per member, per the body. | +| **ACCT-10** | A16 β€” Enable push showed On before the server held the device. | β€” | Push enable control. | **Fixed:** shows On only once the server holds the device, per the body. | +| **ACCT-11** | A5 β€” Web push dispatch had no per-push deadline, reply cap, overall budget or device limit. | β€” | Push dispatch. | **Fixed:** each web push has an absolute deadline and a reply cap; dispatch has an overall budget; an account's stalling devices cost one wait, not one each; an account keeps at most 10 devices, per the body. | +| **ACCT-12** | A4 β€” Mail was sent to kid-login addresses (`kids.bubaly.app`), an unregistered domain. | β€” | Digest, invites, welcome email and the shared senders. | **Fixed:** no mail is sent to a kid-login address, per the body. | +| **ACCT-13** | A17 β€” The welcome email could go to an unverified address and repeat. | β€” | Welcome email. | **Fixed:** only to the verified sign-in address, at most once per 30 days, per the body. | +| **ACCT-14** | A2, A10, A11, A13, A20 β€” Onboarding turned failed reads into success, overwrote preferences, and duplicated rows on retry. | β€” | Onboarding reset and finalize; progress row. | **Fixed:** reset and finalize merge into preferences so App Lock and nav survive; a retried finalize updates the rows it added; an unreadable progress row fails instead of reporting success, per the body. | +| **ACCT-15** | A14 β€” The notifications, settings and announcements insights asked the model about reads that failed. | β€” | Insights. | **Fixed:** answer "could not load", per the body. | +| **ACCT-16** | Follow-up β€” A5 push dispatch bounds (the last open P2). | P2 | Push dispatch; the body says the push send floor test fails with the source reverted. | **Fixed (follow-up commit):** the bound is the time each recipient and each family keeps the run waiting, slow answers and endpoint lookups included; one account gets 18 s (15 s, then 3 s after a slow batch) and one family half of the run's 45 s; each web send has its own deadline cut to what its recipient, family and the run have left and is receipted when answered; a send is not started with under a second left, so that device stays pending for the next run, per the body. | +| **ACCT-17** | Follow-up β€” A7 every road to a manager role for a kid login. | β€” | Onboarding finish; `createChildLoginAction`; admin console create family; the body says the kid-login link race and both admin create-family refusals fail with the source reverted. | **Fixed (follow-up commit):** a replayed onboarding finish writes a manager role onto an existing member only through a compare-and-set on `user_id IS NULL` and refuses a row with a kid login; `createChildLoginAction` links only onto a row whose role is still not a manager's; admin create family refuses a kid login as the owner, per the body. | +| **ACCT-18** | Follow-up β€” A11 a retried onboarding finish. | β€” | Onboarding finish; the body says the corrected invite role test fails with the source reverted. | **Fixed (follow-up commit):** members and invites are keyed by the draft's own ids so a retry reconciles the same rows; a corrected invitee role is written onto the still-pending invite, per the body. | +| **ACCT-19** | Follow-up β€” A17, A1, A4 leftovers. | β€” | `app_metadata`; Settings; both direct-provider marketing senders. | **Fixed (follow-up commit):** the welcome email's once-per-30-days record lives in `app_metadata` and is written before the send; Settings shows pending invites to managers only; both marketing senders refuse a kid-login address, per the body. | +| **ACCT-O01** | A4 β€” `bubaly.app` is unregistered, so kid-login addresses are synthetic. | β€” | DNS and a migration of existing sign-in emails, per the body. | πŸ”„ IN PROGRESS β€” owner action: register `bubaly.app`, or move the kid-login domain to one Bubaly owns with a migration of existing sign-in emails; until then password-reset mail to those addresses belongs to the sign-in lane. | +| **ACCT-O02** | A7 β€” "A kid login is never a parent or adult" is enforced by the action and both editors, not the database. | β€” | Held migration 0512, per the body. | πŸ”„ IN PROGRESS β€” owner action: release 0512. | +| **ACCT-O03** | A9 β€” Dismissal is app-only. | β€” | Needs a `dismissed_at` column or a dismissals table read by the generator, per the body. | πŸ”„ IN PROGRESS β€” owner decision: choose a durable dismissal. | +| **ACCT-O04** | Optional β€” `accept_invite` does not expire sibling invites in SQL. | β€” | SQL, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional). | +| **ACCT-O05** | Optional β€” `/join` sends no `Referrer-Policy` response header. | β€” | Config, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional). | +| **ACCT-O06** | Optional β€” a web push socket is not pinned to the address that was checked. | β€” | Config, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional). | + +Left as is, per the body: two families, each with two never-answering accounts, can still use the run's 45 s between them, so a third family waits for the next push run (every 2 hours); an onboarding insert racing a kid-login link created within the same few milliseconds, which held 0512 closes along with the admin edit's lack of a `user_id` guard (super admin only). + +## Money follow-ups β€” audit lane, PR #1044 β€” 2026-10-13 + +Result: **5 πŸ›  FIXED + PASS (the three places the second review of #1043 found still adding up part of a period, and that review's two small gaps on the Payments page; shown in 4 rows as the body groups them), 2 πŸ”„ IN PROGRESS (owner items).** Each fix has a test that fails without it. Validation per the body: tsc, eslint on every changed file, the i18n gate and `generate-mobile-assistant-messages.mjs --check`; all 234 source-scanning test files in UTC; the 33 test files that reference a changed module in UTC, Los Angeles and Tokyo (638 tests each); the three finance Playwright specs 45 of 45; negative controls fail on the code before each fix. Merged as `9f55eff43`; no SQL, schedule, configuration or provider change; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **MONEYF-01** | The Payments "this month" tiles (In, Out, Net, Transactions) were summed from the newest-500 list, where next month's scheduled rows come first. | β€” | `components/finance/payments-view.tsx`; `tests/money-screens-add-up-every-row-in-its-period.test.ts`: 600 October rows plus next month's rent read `-$600.00` (`-$470.00` before); a teen's 600 own rows beside 50,000 household rows stay whole only while the month read is narrowed to their rows. | **Fixed:** the tiles are summed from their own read of the family's month, bounded at both ends, narrowed to a member's own rows as the list is, paged by id, and failing past 50,000 rows instead of showing a smaller total, per the body. | +| **MONEYF-02** | A month heading's total could be partial when a full list cut the month short. | β€” | Same file and test: a past month cut short shows no heading total; a list that is not full totals every month; a month past the ceiling shows the error. | **Fixed:** a month heading's total is the month's or none; this month, unsearched and unfiltered, is totalled from the month's own read so it matches the tiles, per the body. | +| **MONEYF-03** | The Operating Index's overspent-budget count and the Family Signals budget-drift signal judged budgets over the wrong window, counted next month's scheduled rent as overspend, and counted a later duplicate budget twice. | β€” | `lib/operating-index/*`, `lib/intelligence/hard-signals*`; `tests/the-index-and-the-signals-judge-the-budget-the-family-keeps.test.ts` drives the real `buildSnapshot` and `runSignalDetection`, 11 of its 12 cases fail on the old code. | **Fixed:** each budget is judged in its own period on the family's day, with both ends and Sunday weeks, one budget per category; spending is read paged by id across the current weekly, monthly and yearly windows; the Family Signals action and the model-refresh cron pass the family's time zone; the operating-index, hard-signals and snapshot unit tests were moved to the family's day, per the body. | +| **MONEYF-04** | The phone's budget card showed no period per line and did not say what its headline was. | β€” | `mobile/src/lib/assistant-core.ts`; a case in `tests/a-money-total-is-the-whole-window-or-none.test.ts` builds the card from real tool output in en-US and de-DE. | **Fixed:** each line shows its period and the subtitle says the headline is the month's budgeted spending, as the web card does since #1043; four `mobileAssistant.*` keys added in the seven base locales and `mobile/src/lib/assistant-messages.json` regenerated, per the body. | +| **MONEYF-O01** | Product decision: the finance service judges a weekly budget over the trailing seven days, while the planner, billing, the twin and the coach use Sunday to Saturday. | β€” | Left as is, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | +| **MONEYF-O02** | The signal scan reads only the current periods' span, so a yearly budget's prior year is not compared. | β€” | Comparing it would need a two-year read, a cost decision, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + +Left as is, per the body (older than this PR): the Payments tile labels are still English literals; that page is not under the i18n gate. + +## Money β€” audit lane, PR #1043 β€” 2026-10-13 + +Result: **20 πŸ›  FIXED + PASS (plus 7 low-severity review leftovers and 1 regression fix), 8 πŸ”„ IN PROGRESS (owner items).** The lane audited the family money modules: budgets, bills, expenses, savings, payments, the money timeline, the tax vault, Family CFO, the wallet (cards, allowances, babysitters, invest) and the AI money coaches. Four lenses (authorization, money integrity, failure surfacing, external content) produced 33 candidates; the 20 most severe were each checked by two skeptical verifiers and all 20 confirmed; the other 13 are low or medium and were not verified in this round. Each fix has a test, driven through the real route, component, action, webhook or service, that fails on `c660f2267`; the fixer counted 59 failing cases there. Validation per the body: tsc, eslint on changed files and the i18n gate; all 234 source-scanning test files in UTC; the 185 test files referencing a changed module in UTC, Los Angeles and Tokyo (two cases in `tests/a-babysitter-payment-stays-in-its-family.test.ts` are expected failures that predate the branch); the finance Playwright specs `bill-schedule-repair`, `finance-read-states` and `recurring-bill-payment` 45 of 45; CI on `1b49fb81a` and `9d344b988` 19 of 19. Merged as `70dbf2163`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **MONEY-01** | M1 β€” Family CFO "Can we afford it?" applied none of its page's gates, so a child, guest, Free or Basic family, or password-only session could get the household's projected lowest balance. | β€” | Family CFO action. | **Fixed:** applies the role, plan and step-up checks the page does, per the body. | +| **MONEY-02** | M7 β€” The money timeline showed children, teens and caregivers the household's liquid balance and projected low. | β€” | Money timeline. | **Fixed:** they see the "kept by parents" state, per the body. | +| **MONEY-03** | M8 β€” Babysitter payouts and totals were shown to every member. | β€” | Babysitters. | **Fixed:** only those who read household money see them, per the body. | +| **MONEY-04** | M9, M10 β€” The AI wallet, child and invest coaches served guests; activation gave wallets to any role; guests could request an allowance or a spend. | β€” | Wallet coaches; activation; allowance and spend requests. | **Fixed:** the coaches refuse a guest before any read or model call; activation gives wallets only to children and teens; guests cannot request, per the body. | +| **MONEY-05** | M2 β€” A second "Set" for the same child created a second allowance rule, so the child was paid twice every period with only one rule visible. | β€” | Allowance rules. | **Fixed:** a second rule is refused; every rule is shown and can be paused, per the body. | +| **MONEY-06** | M3, M5, M4 β€” Budgets and expense splits summed one read the server cuts at 1,000 rows, understating "spent" and overstating "left". | β€” | Budgets; expense splits. | **Fixed:** read every row in the window, paged by id, and fail rather than total a partial read, per the body. | +| **MONEY-07** | M11, M12, M13, M14 β€” Weekly, monthly and yearly limits were added as "this month's budget"; the savings coach compared a month of spending with any period, in Greenwich's month; duplicate budget rows were picked arbitrarily and future-dated rows counted. | β€” | Budget period logic; savings coach. | **Fixed:** each budget is judged in its own window, bounded at both ends, on the family's day; monthly totals use each budget's monthly share with one budget per category, per the body. | +| **MONEY-08** | M15 β€” "Total balance" added credit-card debt as if it were cash. | β€” | Billing views; wallet overview. | **Fixed:** debt on credit accounts is subtracted, per the body. | +| **MONEY-09** | M19 β€” The allowance cron's unchunked manager read failed past about 200 families. | β€” | Allowance cron. | **Fixed:** chunks and pages the read, per the body. | +| **MONEY-10** | M6 β€” A Stripe Issuing refund was posted to the child's wallet as spending. | β€” | Money webhook. | **Fixed:** credited back; only a capture releases a hold; an unknown type throws so Stripe retries, per the body. | +| **MONEY-11** | M20 β€” A failed or slow approve/decline call could leave a child's money held forever. | β€” | Card authorization handling. | **Fixed:** the call is bounded, and a hold is released only when Stripe reports it closed, reversed or expired, per the body. | +| **MONEY-12** | M16, M17 β€” The finance and spending insights turned a failed read into "$0.00". | β€” | Insights. | **Fixed:** refuse with "could not load" and the model is not asked, per the body. | +| **MONEY-13** | M18 β€” The briefing turned a failed bills read into "no bills due". | β€” | Briefing. | **Fixed:** answers 503; merges with the home operations fix that fails closed on every household read, per the body. | +| **MONEY-14** | Seven low-severity review leftovers: the Finances view, Home's Family Finances card and the intelligence spend band read part of the month; the Finances budget bar counted a category twice; the twin judged the wrong budget period; the savings coach's year read was capped; the allowance duplicate warning stayed after a rule was paused; the assistant's budget card named no periods. | Low | Commit `36c2d993d`; each with a test that fails without it, per the body. | **Fixed:** whole-month reads (both ends, paged by id); each category counted once; the twin checks the budget the family keeps in its own period; the savings year read is paged by id; the warning clears once a rule is paused; the budget card names each line's period and labels its headline as the month's budgeted spend, per the body. | +| **MONEY-15** | Regression in `36c2d993d`: a non-parent's twin projection was refused outright when they named a budget. | β€” | Commit `e85fe879a`. | **Fixed:** they get the projection without the Cost line, per the body. | +| **MONEY-O01** | M2 β€” Two concurrent first saves can both pass the action's duplicate check. | β€” | Needs a unique index on `allowance_rules(family_id, child_wallet_id)` after existing duplicate rules are merged, per the body. | πŸ”„ IN PROGRESS β€” owner action: the unique index, after merging duplicates. | +| **MONEY-O02** | M6 β€” Two overlapping deliveries of one refund could both credit. | β€” | Needs a unique key for one `card_refund` per `stripe_ref`; the handler already treats a repeat delivery as done and treats the key's 23505 as success once the key exists, per the body. | πŸ”„ IN PROGRESS β€” owner action: the unique key. | +| **MONEY-O03** | M10 β€” Child wallets that activation already gave to caregiver or guest members still exist. | β€” | Data change, per the body. | πŸ”„ IN PROGRESS β€” owner action: deactivate them. | +| **MONEY-O04** | M20 β€” The new handler for a hold Stripe declined by timeout depends on an event the webhook is not subscribed to. | β€” | Stripe configuration, per the body. | πŸ”„ IN PROGRESS β€” owner action: subscribe the money webhook to `issuing_authorization.created`. | +| **MONEY-O05** | Product decision: the finance service (assistant tool, money context) judges a weekly budget over the trailing seven days while the planner, billing, the twin and the coach use Sunday to Saturday. | β€” | Left as is, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | +| **MONEY-O06** | Product decision: scheduled and failed transactions dated inside a period count as spent, matching the finance service. | β€” | Left as is, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | +| **MONEY-O07** | Product decision: the wallet's "Total balance" includes card credit and rewards. | β€” | `tests/wallet-hub.test.ts` pins it, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | +| **MONEY-O08** | Product decision: sitter contact details stay visible to members. | β€” | Left as is, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + +The body's three follow-ups for the next PR (the Payments page summing "this month" from its newest-500 list; the Operating Index and Family Signals budget checks; the mobile budget card's periods) were fixed in #1044 (MONEYF above). + +## Home operations β€” audit lane, PR #1042 β€” 2026-10-13 + +Result: **19 πŸ›  FIXED + PASS (plus 2 low-severity review leftovers), 5 πŸ”„ IN PROGRESS (owner items).** The lane audited chores, weekend, home and maintenance, devices, vacations and trips, and the briefings and insights built on them. Four lenses found 38 candidates; the 20 most severe were each checked by two skeptical verifiers and 19 confirmed; the other 18 are low severity and were not verified in this round. Each fix has a test, driven through the real route, component, action or service, that fails on `c660f2267`; the first reviewer counted 68 failing cases there. Two review rounds closed all 19 and their two low-severity leftovers are fixed here too. Validation per the body: tsc, eslint on changed files and the i18n gate; all 234 source-scanning test files in UTC; the 102 test files referencing the chores, tasks, weekend or insights code in UTC, Los Angeles and Tokyo; `chore-modal-completion.spec.ts` (Playwright) 16 of 16; the coordinator's follow-ups (feed deadline, status writes, cross-zone all-day) each covered by a held-clock or integrated test that fails on the head before its fix. Merged as `63e12d464`; no SQL, schedule, configuration or provider change; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **HOMEOPS-01** | S1 β€” Deleting a child's assignment of a repeating chore was undone by the daily respawn sweep, which recreated it under the key the delete freed, while the board said "Chore removed". | β€” | Chore board delete; respawn sweep. | **Fixed:** a repeating chore that is only this child's is retired before the delete (switched back on if the delete is refused); when the sweep will still put it back the board says "Removed for now. This repeating chore is still active, so it will come back for this child.", using the sweep's own test for this chore and child, per the body. | +| **HOMEOPS-02** | S2 β€” The board dropped a failed chores read, so Approve wrote 0 points, toasted "+0 pts" and skipped the respawn. | β€” | Chore board; Approve. | **Fixed:** the read failure shows the error state and Approve refuses a row whose chore did not load, per the body. | +| **HOMEOPS-03** | S3 β€” A child's note reached the AI chore reviewer unfenced and its verdict could auto-approve and pay out. | β€” | AI chore reviewer; auto-approve threshold. | **Fixed:** title, instructions and note are fenced as untrusted text and the reward amount is capped; never auto-approved unless the reviewer saw an image; a non-manager cannot set the threshold, per the body. | +| **HOMEOPS-04** | S6 β€” The board's Redo always failed. | β€” | Chore board Redo. | **Fixed:** uses a manager-only reject that moves only a submitted chore to rejected, per the body. | +| **HOMEOPS-05** | S7 β€” Deleting an assignment left the child's proof photos and videos in storage. | β€” | Proof media storage. | **Fixed:** released once the delete is confirmed, per the body. | +| **HOMEOPS-06** | S12 β€” The board read assignments in one read that stopped at 1,000 rows, so all-time points, streaks and reward progress were wrong for older families. | β€” | Chore board assignment read. | **Fixed:** pages by id, per the body. | +| **HOMEOPS-07** | S4 β€” The RSS parser took quadratic time on a hostile feed and blocked the server. | β€” | Weekend planner RSS parser. | **Fixed:** scans in one forward pass with item and field caps, per the body. | +| **HOMEOPS-08** | S11 β€” All-day events were stored at UTC midnight, filed under the previous day and dropped early. | β€” | Weekend planner event storage. | **Fixed:** a date with no time is a calendar date, stored midnight to midnight in the family's zone whichever zone the provider files the venue in, and listed under that day as "All day", per the body. | +| **HOMEOPS-09** | S13 β€” When every event source failed the route answered 200 "No events found"; a failed saved feed went unreported when another source answered; deferred feeds were reported as unreachable. | β€” | Weekend search route. | **Fixed:** answers 502 with the reason; a failing saved feed is reported; feeds left for the next search get their own notice; the planner re-reads its events and feeds after a search, per the body. | +| **HOMEOPS-10** | S14 β€” The planner read every event ever discovered, unordered. | β€” | Weekend planner read. | **Fixed:** reads upcoming events, paged, plus those a plan points at, per the body. | +| **HOMEOPS-11** | S18 β€” Each search read every saved feed at once, with no deadline. | β€” | Weekend feed search. | **Fixed:** at most 12 feeds, oldest first, four at a time; each feed at most 10 s and the search at most 25 s including status writes; a feed still reading at the deadline is left for the next search and its late answer changes nothing; a never-answering feed is recorded as tried and falls to the back; a status write that throws no longer stops the queue, per the body. | +| **HOMEOPS-12** | S8, S9 β€” The packing Smart list and the devices screen never re-read their unpublished tables, so a second press duplicated rows. | β€” | Packing Smart list; devices screen. | **Fixed:** re-read after every confirmed write and check for an existing master list, per the body. | +| **HOMEOPS-13** | S10 β€” A backdated service visit turned `last_serviced_on` back. | β€” | Home maintenance service visits. | **Fixed:** no longer; deleting the visit that set it falls back to the latest one still on file, per the body. | +| **HOMEOPS-14** | S17 β€” Trip overview saved a readiness score computed from reads that failed. | β€” | Trip overview. | **Fixed:** no longer saved, per the body. | +| **HOMEOPS-15** | S5 β€” The `home` insight did not apply the Plus plan gate its page has. | β€” | `home` insight. | **Fixed:** applies it, per the body. | +| **HOMEOPS-16** | S15 β€” The `home`, `weather` and `chores` insights built a prompt from reads that failed. | β€” | Insights. | **Fixed:** no longer, per the body. | +| **HOMEOPS-17** | S16 β€” The daily and weekly briefings said "0 chores" or "nothing due" when a household read failed. | β€” | Briefings. | **Fixed:** answer 503, per the body. | +| **HOMEOPS-18** | S19 β€” A member-typed trip title reached the agent planner unfenced. | β€” | Agent planner. | **Fixed:** fenced as data, per the body. | +| **HOMEOPS-19** | Two low-severity review leftovers. | Low | Not named individually in the body; its validation names the coordinator's follow-ups (feed deadline, status writes, cross-zone all-day) as each covered by a test red before the fix. | **Fixed:** in this PR, per the body. | +| **HOMEOPS-O01** | S1 β€” Stopping a shared repeating chore for one child has no tombstone the respawn sweep reads. | β€” | Needs a `removed` task status or a per-member stop table, per the body. | πŸ”„ IN PROGRESS β€” owner action: a tombstone the sweep reads; until then the board says it will come back. | +| **HOMEOPS-O02** | S3 β€” Chores RLS lets a child write `auto_approve_score` and `proof_required` directly through PostgREST. | β€” | Needs a trigger or column guard limited to `can_manage_family`, per the body. | πŸ”„ IN PROGRESS β€” owner action: the trigger or column guard. | +| **HOMEOPS-O03** | S8 (optional) β€” A second master packing list is prevented only in the app. | β€” | Partial unique index on `vacation_packing_lists(vacation_id) WHERE is_master`, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional). | +| **HOMEOPS-O04** | S18 β€” No per-family cap on `weekend_feeds` rows. | β€” | Needs a trigger or check, per the body. | πŸ”„ IN PROGRESS β€” owner action: the cap. | +| **HOMEOPS-O05** | S14 β€” Nothing prunes old `weekend_events`. | β€” | Needs a cron entry, per the body. | πŸ”„ IN PROGRESS β€” owner action: a pruning cron. | + +## Meals and food β€” audit lane, PR #1041 β€” 2026-10-13 + +Result: **19 πŸ›  FIXED + PASS (plus 4 low-severity review leftovers), 2 πŸ”„ IN PROGRESS (owner items).** The lane audited meals, recipes, pantry, nutrition, Fridge Chef, kitchen, dining, grocery and the food hub with four lenses (authorization, integrity, failure surfacing, food safety); each finding was confirmed by two skeptical verifiers and one candidate was refuted. Each fix has a test, driven through the real route, component, tool or service, that fails on `2b5caab76`; 66 cases fail there. A review round closed all 19 and its four low-severity leftovers are fixed too (`7c5a399dc`). Validation per the body: full tsc, `eslint --no-ignore --max-warnings=0` on changed files and the i18n gate; the full vitest suite in UTC, 2,333 files pass (3 Node-version checks fail locally on Node 22; CI runs 24.21); new and changed tests also in America/Los_Angeles and Asia/Tokyo (222 each); `weekly-meal-planner.spec.ts` 69 of 69 including the new left-out message; the leftovers' tests fail on the audit fix's head (10 vitest cases and 1 e2e case). 11 new copy keys in en-US, de, es, fr, it, nl and pt. Merged as `52d951358`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **MEALS-01** | S3 β€” Family Chef never read the family's recorded allergies, so a peanut-allergic household could get a week of peanut dinners. | β€” | Family Chef. | **Fixed:** reads allergies, diets and dislikes and fails closed (503) if it cannot; puts them in the prompt as fenced data and screens the reply; a conflicting dish is dropped and a grocery item swapped or dropped, per the body. | +| **MEALS-02** | S4 β€” The Meal Planner wrote a whole week into `meal_plans` from the free-text "dietary" box alone. | β€” | Meal Planner. | **Fixed:** reads the recorded constraints and fails closed if it cannot; unsafe library dishes are not offered to the model; any assignment that hits an allergy is dropped before the write; the family is told how many were left out, and when all were the error says so, per the body. | +| **MEALS-03** | S19 β€” Fridge Chef's "May contain" was a raw substring test: "Peanuts" missed "peanut butter" and "Tree nuts" missed almonds. | β€” | `allergenConflict`; "Add to grocery". | **Fixed:** reads each note, singularises words and applies the allergen table, steps included; an exemption ("oat milk") covers only the words around one mention, so "Simmer the rice in whole milk" is flagged; "Add to grocery" swaps or leaves off allergens and says which, per the body. | +| **MEALS-04** | S5 β€” The chef, the planner and the assistant's shopping context offered expired pantry items and leftovers past their use-by date as "use these up". | β€” | Chef; planner; shopping context. | **Fixed:** only still-good items are offered; expired ones are listed as "check or discard", per the body. | +| **MEALS-05** | S2, S7 β€” Fridge Chef "Plan for dinner" added a second dinner to an occupied slot with raw inserts; the meals page's week read then failed on every retry and a failed plan write left an orphan dish. | β€” | Fridge Chef; `setSlot`. | **Fixed:** goes through `setSlot`, which replaces the slot, reuses the dish and rolls back on failure, per the body. | +| **MEALS-06** | S1 β€” AI meal tools turned omitted ingredients into `[]`, so a library dish named by the assistant was cloned with no ingredients, which blocked the shopping list, and any plan by `meal_id` was refused. | β€” | AI meal tools. | **Fixed:** ingredients are sent only when the model gives them, per the body. | +| **MEALS-07** | S8 β€” The chat assistant and the voice bridge added grocery items with raw inserts, so "Milk" already on the list got a second line. | β€” | Chat tool; voice bridge; `addItems`. | **Fixed:** chat goes through `addItems`; voice skips open duplicates, per the body. | +| **MEALS-08** | S9 β€” A plural pantry line ("Onions") created a duplicate next to "Onion". | β€” | Pantry put-away. | **Fixed:** no longer, per the body. | +| **MEALS-09** | S12 β€” Pantry quantities were added across different units. | β€” | Pantry put-away. | **Fixed:** never added across units; a same-unit purchase still adds, with plurals folded (box/boxes, loaf/loaves), per the body. | +| **MEALS-10** | S10 β€” The pantry editor wrote back the quantity it was opened with over +/- taps made meanwhile. | β€” | Pantry editor. | **Fixed:** no longer, per the body. | +| **MEALS-11** | S11 β€” AI recipe variants lost their recipe's servings, so later scaling was wrong. | β€” | Recipe variants. | **Fixed:** keep their recipe's servings, per the body. | +| **MEALS-12** | S13 β€” The kitchen leftovers card read the wrong leftovers and hid a failed read. | β€” | Kitchen leftovers card. | **Fixed:** reads tonight's fresh leftovers in use-by order and says when it could not read them, per the body. | +| **MEALS-13** | S14 β€” Food hub cards showed "No dinners planned yet" or "Nothing expiring soon" on a failed read. | β€” | Food hub cards. | **Fixed:** every card says when its read failed, per the body. | +| **MEALS-14** | S15 β€” The meal vote page read options and ballots cut at 1,000 rows. | β€” | Meal vote page. | **Fixed:** reads every option and ballot of the votes shown, paged, per the body. | +| **MEALS-15** | S16 β€” A recipe-search provider outage answered "No recipes found". | β€” | Recipe search. | **Fixed:** answers 502, per the body. | +| **MEALS-16** | S17 β€” The chef planned around busy nights from a 40-event cap and an unordered sample of expiring items. | β€” | Chef calendar and pantry reads. | **Fixed:** reads the whole calendar and the expiring items directly, per the body. | +| **MEALS-17** | S18 β€” An unreadable model reply from Fridge Chef or the flyer scan answered "No food spotted". | β€” | Fridge Chef; flyer scan. | **Fixed:** answers 502, per the body. | +| **MEALS-18** | S6 β€” The AI insights "grocery" kind was not gated like the "shopping" kind it aliases. | β€” | Insights gate. | **Fixed:** gated the same way, per the body. | +| **MEALS-19** | Four low-severity review leftovers. | Low | Commit `7c5a399dc`; the body does not name them individually; their tests fail on the audit fix's head (10 vitest cases, 1 e2e case). | **Fixed:** in this PR, per the body. | +| **MEALS-O01** | Meal plan slots can still be stacked by another writer. | β€” | Needs a deduplication of `meal_plans` and a unique `(family_id, plan_date, meal_type)` index; the code routes Fridge Chef through `setSlot`, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional hardening): the unique index after deduplication. | +| **MEALS-O02** | The pantry put-away lookup is a normalised-stem prefilter rather than an exact match. | β€” | A normalised-name column or index on `pantry_items`, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional hardening): the column or index. | + +Known remaining, per the body: the voice bridge (S8) skips duplicates against the list's open lines itself and has no `addItems`-style read-back; the recipe search "partial results" notice (S16) cannot be reached while TheMealDB is the only registered provider; 15 lower-ranked findings beyond the verified top 20 are not fixed. + +## Kids and learning β€” audit lane, PR #1040 β€” 2026-10-13 + +Result: **20 πŸ›  FIXED + PASS (plus 2 low-severity review leftovers), 6 πŸ”„ IN PROGRESS (owner items).** The lane audited to-dos, homework, rewards, timetable, goals, library, language, focus and projects with four lenses (authorization, integrity, failure surfacing, exposure); each finding was confirmed by two skeptical verifiers. Each fix has a test, driven through the real component, action, route or service, that fails without it; 51 of the first round's 74 new tests fail on `14c323904`. A review round closed what it found and its two low-severity leftovers are fixed too (`f2f89304f`). Validation per the body: full tsc, `eslint --no-ignore --max-warnings=0` on changed files and the i18n gate; the full vitest suite in UTC, 2,316 files pass (3 Node-version checks fail locally on Node 22; CI runs 24.21); new and changed tests also in America/Los_Angeles and Asia/Tokyo (80 each); the changed Playwright specs (rewards, to-dos, projects) passed; the review cases AUD-1040-COMPLETED-SEARCH and AUD-1040-LEDGER-PAGING fail on `a5de9af11` (5 cases) and AUD-1040-LIBRARY-BOUNDARIES on `f2f89304f` (9 vitest cases, plus 2 on the real worker handlers in Chromium via `tests/e2e/service-worker-cache.spec.ts`, 75 of 75 with the fix). 5 new copy keys in seven locales; three Playwright fixtures (`rewards-ledger`, `rewards-readback-review`, `todo-modal-completion`) now honour offset, limit and count as PostgREST does, with no assertion weakened. Merged as `c660f2267`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **KIDS-01** | S1 β€” The to-do list was one unpaged read, oldest due date first; once done tasks passed the 1,000-row cap, the newest-dated and every undated open task disappeared. | β€” | To-do list read; commit `34c9ddf5f` for the Completed-tab search. | **Fixed:** open tasks are read whole; finished tasks listed newest first up to 200 with an exact count and a note when only some are shown; a Completed-tab search asks the database over all of them, per the body. | +| **KIDS-02** | S8 β€” Today's and upcoming homework had the same cap, and a student with all work still to do was told "No homework yet". | β€” | Homework reads. | **Fixed:** read whole; Completed % comes from exact counts, per the body. | +| **KIDS-03** | S9 β€” Reward balances were summed over an unordered 1,000 rows of the ledger, so a child's balance came out short and Redeem and Approve were disabled. | β€” | Rewards page, kid dashboard, rewards coach; `readAllById`; commit `34c9ddf5f`. | **Fixed:** approved chores and redemptions are paged and the read fails closed past its ceiling; the ledger readers page on `id > last` so a row written between pages is never counted twice, per the body. | +| **KIDS-04** | S12 β€” Project materials were read oldest first with `.limit(1000)` and quotes cheapest first with `.limit(600)`, so new materials and dear quotes dropped out. | β€” | Projects reads. | **Fixed:** both read whole, per the body. | +| **KIDS-05** | S2 β€” One large podcast pushed every hand-added book off the library page, which then said "No books yet". | β€” | Library page. | **Fixed:** episodes and books are separate reads with "showing N of M" notes, per the body. | +| **KIDS-06** | S5 β€” The kid dashboard's "points to spend" ignored redemptions and an unreadable ledger showed a number. | β€” | Kid dashboard. | **Fixed:** subtracts redemptions; an unreadable ledger shows "β€”" and nothing is marked affordable, per the body. | +| **KIDS-07** | S11 β€” The rewards coach's "total points earned" came from 20 arbitrary rows. | β€” | Rewards coach. | **Fixed:** reads the whole ledger; the prompt gives earned, spent and available per member, per the body. | +| **KIDS-08** | S13 β€” Any member, a child included, could subscribe the family to a feed or attach any audio or video address, which then streamed from Bubaly's own origin past a parent's host filter and was kept in the offline cache. | β€” | Library actions; media route; offline library cache; commit `a5de9af11`. | **Fixed:** subscribing, unsubscribing and attaching a media address are for parents and adults (anyone can still add a title or page link); the media route refuses a child or teen anything not named as audio, including video and `application/octet-stream`; the offline cache keeps and serves only named audio, across a logout and a new sign-in, per the body. | +| **KIDS-09** | S14, S15 β€” A guest got the homework and timetable AI summaries the school summary refuses over the same rows. | β€” | Homework and timetable summaries. | **Fixed:** a guest is refused before any read and the modules hide the button, per the body. | +| **KIDS-10** | S16 β€” A guest could add, rename, reassign and tick off any family to-do, and Focus Mode wrote `is_done` from the browser. | β€” | To-do actions and services; to-do page; Focus Mode. | **Fixed:** the actions and services refuse a guest; the page offers a guest no add, edit, tick-off, quick-add or new-list control; a guest's Focus queue leaves the family to-dos out, per the body. | +| **KIDS-11** | S3, S4 β€” The "Shared with family" box on a list did nothing: private lists were shown to every member and sent to the AI. | β€” | To-do list dialog. | **Fixed:** the box is removed and the dialog says lists are family-wide; new lists record their creator, per the body. | +| **KIDS-12** | S6 β€” The AI homework planner printed due dates as the UTC day, so an evening deadline moved a day later. | β€” | AI homework planner. | **Fixed:** prints them on the family's clock, per the body. | +| **KIDS-13** | S17 β€” Homework's "Due today" used the device's day. | β€” | Homework. | **Fixed:** uses the family's day, per the body. | +| **KIDS-14** | S10 β€” The AI insights for homework, todos, goals, timetable and rewards told the model "none" on a failed read. | β€” | Insights. | **Fixed:** refuse to answer on a failed read, per the body. | +| **KIDS-15** | S7 β€” A failed library progress read rendered every bookmark at 0 and the next play overwrote the stored position. | β€” | Library page and player; commit `a5de9af11`. | **Fixed:** the page says so; the player saves no position until it knows the old one; a row that played before the place arrived writes nothing and its next Play starts from the recovered place, per the body. | +| **KIDS-16** | S19 β€” A finished episode stayed "completed" forever, so replaying it lost your place on every visit. | β€” | Library player; commit `a5de9af11`. | **Fixed:** a replay's "not finished" rides every save until one lands, so a failed first write cannot be overtaken by a position-only one, per the body. | +| **KIDS-17** | S18 β€” A double tap in language review logged a sitting twice and graded twice. | β€” | Language review. | **Fixed:** no longer, per the body. | +| **KIDS-18** | S20 β€” The nightly library cron sent `created_by ''` when a subscriber's account was gone, so that feed's ingest failed every night. | β€” | Library cron. | **Fixed:** no longer, per the body. | +| **KIDS-19** | Two low-severity review leftovers. | Low | Commit `f2f89304f`; the body does not name them individually; AUD-1040-LIBRARY-BOUNDARIES fails on that head before its fix. | **Fixed:** in this PR, per the body. | +| **KIDS-O01** | S16 β€” RLS on `todo_items` (0479) and `todo_lists` (0015/0106) is still `is_family_member`, so a guest can write through PostgREST directly. | β€” | Needs `trg_todo_items_not_a_guests` and `trg_todo_lists_not_a_guests` using `public.household_write_is_not_a_guests()`, per the body. | πŸ”„ IN PROGRESS β€” owner action: the two triggers. | +| **KIDS-O02** | S13 β€” The `library_feeds` and `library_items` INSERT/UPDATE policies have no role predicate. | β€” | Today the server actions and the page enforce it, per the body. | πŸ”„ IN PROGRESS β€” owner action: managers-only policies or a not-a-child trigger. | +| **KIDS-O03** | S13 β€” Explicit episodes cannot be filtered. | β€” | Needs a column such as `library_items.explicit`; the ingest could then read ``, per the body. | πŸ”„ IN PROGRESS β€” owner action: the column, then ingest, page and media route changes. | +| **KIDS-O04** | S3, S4 β€” Private lists, only if wanted. | β€” | `(is_shared OR created_by = own member id)` on the `todo_lists` and `todo_items` SELECT/UPDATE policies, with matching app filters; lists already `is_shared=false` stay family-wide, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | +| **KIDS-O05** | S9 (optional) β€” The whole ledger is sent to the browser to sum balances. | β€” | A security-invoker RPC or view summing per-member balances as 0439 does, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional). | +| **KIDS-O06** | The library is not behind a feature switch. | β€” | A plan catalog decision; the role gate and the media refusal cover the child-safety part, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + +Known remaining, per the body: the 1,000-row cutoffs assume production keeps PostgREST's default `db-max-rows` of 1,000, as `lib/supabase/read-all.ts` documents; 12 lower-ranked findings beyond the verified top 20 are not fixed. + +## Sensitive records β€” audit lane, PR #1039 β€” 2026-10-13 + +Result: **17 πŸ›  FIXED + PASS, 4 πŸ”„ IN PROGRESS (owner items).** The lane audited tax vault, paperwork, documents, files (cloud, shared, vault), binder, medical, dental, care and family emergency with four lenses (authorization, integrity, failure surfacing, exposure); each finding was confirmed by two skeptical verifiers. Each fix has a test, driven through the real page, action, route or module, that fails on `a11915fcd`; 42 cases fail there. Two adversarial review rounds then closed what they found. Validation per the body: full tsc, `eslint --no-ignore --max-warnings=0` on the 46 changed files and the i18n gate, all clean; 371 test files (5,549 tests) pass in UTC, covering every new and changed test, every test referencing a changed module and all source-scanning ratchets; the 15 new and changed test files (118 tests) also pass in America/Los_Angeles and Asia/Tokyo; the review round reverted each fix file in turn and saw the expected failures. 9 new copy keys in seven locales. Merged as `2b5caab76`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **SENS-01** | S1, S5 β€” The twin projection, running on the service role from the daily cron, the on-read auto-refresh or a parent's rebuild, copied health providers ("Dr. X (Psychiatry)" plus a "member sees provider" edge), paperwork titles and amounts, bills, account names, warranties, places, locations and renewals into `graph_entities`, which every member including guests reads (0129) and which the assistant's context reads. | β€” | `runTwinProjection`; `graph_entities` (0129); `AUDIENCE_RESTRICTED_REF_TABLES`. | **Fixed:** the projection no longer reads those tables; they are listed in `AUDIENCE_RESTRICTED_REF_TABLES`, so the next run prunes the nodes earlier runs wrote and their edges cascade; hand-made nodes stay, per the body. | +| **SENS-02** | S2, S15 β€” Prep plan generation wrote "Renew: Dad's green card" or "Renew: Custody order" into family-wide `prep_plans` for Secure Vault and sensitive documents. | β€” | Prep plan generator; `prep_plans`. | **Fixed:** a sensitive document is titled "Renew: Private document"; already-dismissed plans are renamed with a confirmed update and their status left alone, per the body. | +| **SENS-03** | S6, S16 β€” The household inbox listed every member's paperwork to teens, children and caregivers. | β€” | `loadInboxQueue`; the paperwork page's own narrowing. | **Fixed:** `loadInboxQueue` narrows to what they added, before the limit is applied, per the body. | +| **SENS-04** | S17 β€” Non-managers saw every member's health visits and immunizations, on screen and in the "medical" insight. | β€” | Health visits and immunizations; "medical" insight. | **Fixed:** non-managers see their own records and the ones they recorded, per the body. | +| **SENS-05** | S3 β€” Paperwork triage read the due date's year and urgency off the family's day, so a slip due today but captured in the evening in the Americas got next year's date. | β€” | Paste action, capture, link, inbound email, email attachments and contact-center filing. | **Fixed:** reads them on the family's day key across all those paths, per the body. | +| **SENS-06** | S7 β€” Document expiry counted days off the family's day, so a passport could be marked expired on its last valid day. | β€” | Home's "needs you"; the assistant's pending decisions; the dashboard's insight. | **Fixed:** counts days between day keys, so a passport stays valid through its last day in Los Angeles and in Tokyo, per the body. | +| **SENS-07** | S8 β€” Deleting an insurance policy left its card photos; edit, replace and close-without-saving mishandled uploads. | β€” | Policy card photos. | **Fixed:** both photos are removed once the row delete is confirmed and the page says if one survives; edit, replace and close manage uploads per side, per the body. | +| **SENS-08** | S9 β€” The tax vault uploaded before validating and kept the file on a failed insert. | β€” | Tax vault upload. | **Fixed:** checks amount and year first, confirms the insert, and removes the uploaded W-2 or 1099 on any error, refusal or throw, per the body. | +| **SENS-09** | S10 β€” Saving a medical profile overwrote a concurrent edit such as a newly added allergy. | β€” | Medical profile save. | **Fixed:** writes only the fields changed since the form opened, per the body. | +| **SENS-10** | S4 β€” The Health Profiles section said "No profile on file." for a member whose profile RLS hides. | β€” | Health Profiles section. | **Fixed:** a non-manager is told "Kept for parents and adults. Ask one of them."; their own card is unchanged, per the body. | +| **SENS-11** | S11 β€” The AI insights for tax, documents, care, medical and binder told the model "nothing stored" on a failed read. | β€” | Insights. | **Fixed:** refuse to answer; the documents prompt reads titles and the tax prompt counts deductibles by category, per the body. | +| **SENS-12** | S12 β€” Open paperwork items past the 500 newest disappeared and the inbox could read "Inbox zero". | β€” | Paperwork inbox. | **Fixed:** reads every open item, paged; done and archived items stay capped, per the body. | +| **SENS-13** | S13 β€” The File Manager and the Files hub stopped at PostgREST's 1,000-row cap, so older files left the list, search and folders. | β€” | File Manager; Files hub. | **Fixed:** page past the cap, ordered by `created_at` then `id`, per the body. | +| **SENS-14** | S14 β€” A caregiver whose reads 0481 narrows was told "No visits logged" or "No immunizations recorded". | β€” | Health visits and immunizations for caregivers. | **Fixed:** non-managers get a note and their own empty state, per the body. | +| **SENS-O01** | `prep_plans` rows already titled after a sensitive document whose expiry has passed the generator's 60-day horizon keep their real titles. | β€” | The generator never re-emits those plans; the body gives a one-time `update public.prep_plans … set title = 'Renew: Private document'` scrub. | πŸ”„ IN PROGRESS β€” owner action (optional cleanup): run the scrub. | +| **SENS-O02** | Restricted graph nodes written before this change stay until each family's next projection run. | β€” | The body gives a `delete from public.graph_entities where ref_table in (…) and provenance source = 'projection'` statement; edges cascade through the 0129 FKs. | πŸ”„ IN PROGRESS β€” owner action (optional cleanup): run the delete. | +| **SENS-O03** | The graph has no per-row audience. | β€” | Any projected table later narrowed by role must be added to `AUDIENCE_RESTRICTED_REF_TABLES` until a migration gives the graph an audience column and policy, per the body. | πŸ”„ IN PROGRESS β€” owner action: the audience column and policy. | +| **SENS-O04** | `materializePaperworkActionAction`, `draftPaperworkReplyAction` and `setPaperworkStatusAction` still accept another member's paperwork id from a non-manager, and RLS exposes the rows over REST. | β€” | The inbox no longer hands those ids out; held migration 0514, per the body. | πŸ”„ IN PROGRESS β€” owner action: ship 0514. | + +Known remaining, per the body: showing a caregiver the children's allergies through `family_allergies()` was optional in S4 and is not done; one disputed finding and 12 low-severity findings beyond the verified top 20 are not fixed. + +## Family-life modules β€” audit lane, PR #1031 β€” 2026-10-13 + +Result: **40 πŸ›  FIXED + PASS, 4 πŸ”„ IN PROGRESS (owner items).** The lane audited pets, vehicles and insurance, rides, screen time, habits, independence, journal, relationship, life events, moments, wish lists, voting, tracked subscriptions and utility bills (F1–F40). Each finding was confirmed by two skeptical verifiers; each fix comes with a test that fails without it; adversarial review rounds and the repository coordinator's source reviews then closed what they found across the commits the body lists (fix `3e9691647`, review round 2 `6dc5fdf7c`, re-fix `55396d19b`, records round `b1a73233b`, relationship race fixes `74199f302`, `f4212b476`, `c0e4ff3c5`, round 4 `8227d2f67`, migration 0486 and its probe `bd57dd0c3` and successors, reminder identity `f0f67a89d`, `3c70a8cc6`, hidden reminder ids `4d8892050` with the coordinator's #1038). Validation per the body: full tsc, `eslint --no-ignore --max-warnings=0` on changed files and the i18n gate; every new and changed test, every test referencing a changed module and the ratchets in UTC, Los Angeles and Tokyo at each round, with the negative controls failing on the previous sources; migration 0486 replayed on PG16 with all 189 probes passing and an idempotent re-apply; CI fully green on the final head `5b50bf1c4`. Merged as `a11915fcd`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **FAMLIFE-01** | F1, F4 β€” Autopilot money and policy cards (subscription charges, renewals, insurance renewals) were shown and pushed to members who should not see them, and an insurance reminder was assigned to the child the policy names. | β€” | New `lib/autopilot/audience.ts`; `lib/autopilot/reminder-cards.ts`; the home dashboard, the Autopilot module, Calm, the proactive slice, `resolveSuggestion`, /home "Completed by Bubaly", the AI dashboard's Today and Coming Up, the briefs and Kitchen Mode. | **Fixed:** subscription charges and renewals go to every member except guests; an insurance renewal goes to the managers plus the member the policy names; the rule covers the scan's pushes, every card list and the reminder copies, with notices sent per reader; an insurance reminder is no longer assigned to the child, per the body. | +| **FAMLIFE-02** | F2, F3, F5 β€” A wish-list owner could learn which wishes were bought (insight and module sort); a partner could see the gifts being planned for them on the Relationship Helper or its AI route. | β€” | Wish Lists insight and module; Relationship Helper and its route. | **Fixed:** neither leaks any more, per the body. | +| **FAMLIFE-03** | F6 β€” Guests could reach `/dashboard/utilities` and its two AI routes. | β€” | `/dashboard/utilities`. | **Fixed:** refused, per the body. | +| **FAMLIFE-04** | F33, F34 β€” The habits, journal and relationship AI calls were not plan-gated; the Celebrations and Wish Lists pages did not apply their tier gate. | β€” | AI routes; Celebrations and Wish Lists pages. | **Fixed:** plan-gated and tier-gated, per the body. | +| **FAMLIFE-05** | The Insurance Hub offered its AI insight to roles the route refuses. | β€” | Insurance Hub. | **Fixed:** offered only to the roles the route answers, per the body. | +| **FAMLIFE-06** | F8, F11, F12, F13, F14, F19, F21, F22, F23 β€” Dates were computed on UTC or the device's day: auto renewal status, the screen-time streak, journal entry dates, subscription last-use, habit streaks, life-event reminders, rental pickup and return times, Celebrations and pet "care needs". | β€” | The modules named. | **Fixed:** each is on the family's day, per the body. | +| **FAMLIFE-07** | F7, F16 β€” Closed polls accepted votes when tapped, and Reopen did not work after a deadline. | β€” | Voting. | **Fixed:** closed polls refuse votes and Reopen works after a deadline, per the body. | +| **FAMLIFE-08** | F9, F26 β€” The odometer could move backwards and negative mileage was accepted. | β€” | Vehicles. | **Fixed:** the odometer only moves forward; negative mileage is refused, per the body. | +| **FAMLIFE-09** | F10 β€” A removed pet's care records stayed in the counts. | β€” | Pets. | **Fixed:** they leave the counts, per the body. | +| **FAMLIFE-10** | F15, F35 β€” A failed options insert left an orphan poll. | β€” | Voting. | **Fixed:** removes its poll, per the body. | +| **FAMLIFE-11** | F17 β€” A relationship date's calendar event did not follow edits and deletes. | β€” | Relationship dates; calendar sync. | **Fixed:** follows them, per the body. | +| **FAMLIFE-12** | F18 β€” Start overwrote an achieved or skipped independence rung and claimed success. | β€” | Independence ladder. | **Fixed:** never overwrites; says "already on the ladder", per the body. | +| **FAMLIFE-13** | F20 β€” Utility savings read old bills. | β€” | Utilities. | **Fixed:** reads the newest bills, per the body. | +| **FAMLIFE-14** | F25 β€” A "0 days before" reminder did not stay 0. | β€” | Life-event reminders. | **Fixed:** stays 0, per the body. | +| **FAMLIFE-15** | F24, F28, F29, F30, F31 β€” The insurance, celebrations, votes and rides insights read wrong columns and tables; poll tallies were cut short. | β€” | Insights. | **Fixed:** read real columns and tables; poll tallies read every option and vote of the polls listed, per the body. | +| **FAMLIFE-16** | F32 β€” Nine insight kinds answered on a failed read. | β€” | Insights. | **Fixed:** refuse to answer, per the body. | +| **FAMLIFE-17** | F36, F37, F38, F39 β€” Failures answered 200 or showed nothing. | β€” | The modules named in the body. | **Fixed:** failures are surfaced, per the body. | +| **FAMLIFE-18** | F27 β€” Habit titles reached a prompt unfenced. | β€” | Habits. | **Fixed:** fenced as untrusted content, per the body. | +| **FAMLIFE-19** | F40 β€” Rides and habit-log reads were unpaged. | β€” | Rides; habit logs. | **Fixed:** paged, per the body. | +| **FAMLIFE-20** | Review rounds β€” relationship profile gating, the calendar sync re-check and the rental refusal; relationship changed-link races (sync unlink, removal against re-link, toggle-off/toggle-on winners). | β€” | Commits `b1a73233b`, `74199f302`, `f4212b476`, `c0e4ff3c5`. | **Fixed (review commits):** per the body. | +| **FAMLIFE-21** | Review round 4 β€” a reminder whose card cannot be resolved; card lists filled before their limit. | β€” | Commit `8227d2f67`. | **Fixed (review commit):** such a reminder goes to the managers alone; card lists are filled from the cards the reader may see before the limit is taken, per the body. | +| **FAMLIFE-22** | Reminder identity and capped pages β€” a scan reminder could resolve to the wrong card; a page the server capped short was taken as the end of the list. | β€” | Commits `f0f67a89d`, `3c70a8cc6`. | **Fixed (review commits):** a scan card records the reminder it wrote and a scan reminder resolves only to that card; a capped page is not taken for the end, per the body. | +| **FAMLIFE-23** | Hidden reminder ids β€” completing, snoozing or deleting a reminder, a run's checks and an approved replay acted on reminders the actor may not be shown. | β€” | The coordinator's #1038 (`950a9304e`) and `4d8892050`. | **Fixed (review commits):** they act only when the actor may be shown the reminder; a hidden id answers exactly as an unknown one, per the body. | +| **FAMLIFE-24** | Kept current with main through #1037. | β€” | Commits `5a2a7b891`, `5b50bf1c4`. | **Fixed (merge commits):** the Display page carries main's open-list grocery reads beside the reminder tile's audience filter; the journal and habits AI routes apply main's closed/trial-ended refusal first, then the plan gate, per the body. | +| **FAMLIFE-O01** | Migration 0486 (`0486_an_autopilot_card_is_read_by_its_audience.sql`), the database half of F1/F4, is in the PR but not applied. | β€” | A restrictive SELECT and a matching UPDATE policy on `autopilot_suggestions`, a trigger pinning `kind`, `source_kind` and `family_id`, an idempotent backfill of legacy insurance cards' `member_id`; probe `docs/audit/an-autopilot-card-is-read-by-its-audience-check.sql` (178 checks with negative controls); the app half gives the same answer with or without it, per the body. | πŸ”„ IN PROGRESS β€” owner action: apply 0486 in the manual release. | +| **FAMLIFE-O02** | F3's database half: `relationship_gift_ideas` is readable by the partner the ideas are for. | β€” | Needs a restrictive SELECT policy, per the body. | πŸ”„ IN PROGRESS β€” owner action: the policy. | +| **FAMLIFE-O03** | F7 β€” A closed poll takes votes at the database. | β€” | Optional trigger, defence in depth, per the body. | πŸ”„ IN PROGRESS β€” owner action (optional): the trigger. | +| **FAMLIFE-O04** | F9, F26 β€” Non-negative mileage and cost are not enforced at the database. | β€” | `NOT VALID` CHECKs on `vehicles` and `auto_service_records`, per the body. | πŸ”„ IN PROGRESS β€” owner action: the CHECKs. | + +Per the body, the coordinator's 0487 (immutable card and reminder origin, trusted reminder creation, a read rule for the reminder tables) follows in its own change; those tables still read family-wide in RLS and the app hides a card's reminders from readers the card is not for. + +## Support batch 3: marketplace orders, AI plan gates, archived lists, crons, referral send limit, assistant chores β€” audit lane, PR #1037 β€” 2026-10-13 + +Result: **15 πŸ›  FIXED + PASS (the fixes the body lists across six Support branches), 3 πŸ”„ IN PROGRESS (owner items).** Support batch 3 merges six Support branches onto main `aca5c9cba` (80 files). Each branch was first brought up to date with main; anything main already fixed was dropped, and each fix kept was shown failing on main's code before it was kept. Code and tests only: no migration, no SQL, no `supabase/` change; tests are synthetic in-memory doubles and no provider, money or email leaves the process. Validation on the merged tree `3fd56252c`: eslint on all changed files exit 0; vitest on the 551 test files that touch changed modules plus every ratchet, guard and i18n test, 9,109 passed plus 2 expected-fail and 0 failed; `tsc --noEmit -p .` exit 0; the full suite, E2E and build were left to CI. The only conflicts were the seven locale files (key-level three-way merge, checked to equal the union of both sides' added keys) and one import line in `lib/assistant/tools.ts`. Merged as `4049f99a9`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **SUPB3-01** | Marketplace β€” a buyer could mark an order returned or complete and was answered `ok`. | β€” | Support branch `marketplace-transactions-7q6vtf`; #771 items 6133850174 and 6133855192; the marketplace fixes were red on main in 28 cases. | **Fixed:** only the seller marks an order returned or complete, per the body. | +| **SUPB3-02** | Marketplace β€” a stale reschedule undid a confirmed pickup. | β€” | Same branch and cases. | **Fixed:** no longer, per the body. | +| **SUPB3-03** | Marketplace β€” cancelling an order left its pickup and calendar event in place. | β€” | Same branch and cases. | **Fixed:** cancelling an order cancels its pickup and takes its event off the calendar, per the body. | +| **SUPB3-04** | Marketplace β€” a price typed as `12,50` was read as 1,250.00. | β€” | Same branch and cases. | **Fixed:** it is 12.50, per the body. | +| **SUPB3-05** | Marketplace β€” an owner accepted an offer without its amount in view. | β€” | Same branch and cases. | **Fixed:** the amount is in view when accepting, per the body. | +| **SUPB3-06** | AI plan gates β€” `/api/ai/chat` was not gated like `/api/ai`. | β€” | Support branch `plan-entitlement-server-7q6vtf`; #771 item 6133903074; red on main 6 of 7. | **Fixed:** gated the same way, per the body. | +| **SUPB3-07** | AI plan gates β€” the free AI helpers (habits, journal, notes, relationship, gift, voice speak and transcribe) served a closed or trial-ended family. | β€” | Same branch; red on main 16 of 24; main's #989 already covered `/api/ai` and the feature gates, so those parts were dropped. | **Fixed:** they refuse a closed or trial-ended family before the rate limiter or the provider, per the body. | +| **SUPB3-08** | Archived shopping lists β€” counts and lists of unchecked groceries included archived lists, in 11 dashboard and assistant places and `app/api/ai/insights`. | β€” | Support branch `data-lifecycle-7q6vtf`; #771 items 6133933536 and 6134043555; `readOnOpenGroceryLists`. | **Fixed:** they read open lists only through `readOnOpenGroceryLists`; the insights route answers 500 rather than prompting from a failed list read, per the body. | +| **SUPB3-09** | Archived shopping lists β€” the source guard did not see reads made through a helper. | β€” | Same branch; the home-asset cleanup from this branch already shipped in #1022 and is not repeated. | **Fixed:** the guard sees them, per the body. | +| **SUPB3-10** | Crons β€” the abandoned-checkout nudge was broken and crons ran for a closed family. | β€” | Support branch `sending-crons-7q6vtf`; #771 item 6133972574. | **Fixed:** the nudge is fixed and crons skip a closed family, per the body. | +| **SUPB3-11** | Sending β€” the notification email was not stamped once sent. | β€” | Same branch. | **Fixed:** stamped once it is sent, per the body. | +| **SUPB3-12** | Sending β€” push receipts were not confirmed. | β€” | Same branch. | **Fixed:** confirmed, per the body. | +| **SUPB3-13** | Sending β€” a retried automation run could resend an email an earlier attempt delivered. | β€” | Same branch; main's automation unsubscribe check superseded the branch's own version. | **Fixed:** it does not resend; a step that throws is still logged `:failed`, per the body. | +| **SUPB3-14** | Referral email daily limit β€” two invitations started together, with nine sends already recorded, both returned `ok` and eleven were recorded. | β€” | Support branch `referral-invite-budget-7q6vtf`; `lib/referrals/core.ts` (`decideReferralEmailAdmission`) and `server.ts`; answers #1022 comment 6127838006 and #771 items 6133742075 and 6134015836; red on main 10 of 16 ("expected 11 to be 10", two emails sent); ten mutants killed; the full-metadata CAS and reward-attempt protections and their tests are unchanged. | **Fixed:** insert-then-admit: a send is admitted only while a re-read after its own write shows at most ten; the one ranked past ten removes only its own stamp and is throttled; the email goes only after admission, per the body. | +| **SUPB3-15** | Assistant chores β€” a teen's assistant could create a chore and assign one to a sibling, because the trust gate passes a teen's low-risk writes. | β€” | Support branch `ai-tool-authorization-7q6vtf`; #771 items 6133894798 and 6134042598; `createChore`, the new `requestChoreAssignment` and chat `add_chore`; red on main in 4 teen cases; the other assistant write tools were audited against their dashboard rules and match. | **Fixed:** they refuse anyone but a parent, adult or the system, as the chores board does, per the body. | +| **SUPB3-O01** | The database still lets either party write `marketplace_orders.status`. | β€” | That half is a separate held migration, being prepared, per the body. | πŸ”„ IN PROGRESS β€” owner action: apply the held migration when it is ready. | +| **SUPB3-O02** | Question flagged on #771: should `payment_failed` and `payment_completed` automations be exempt from marketing opt-out? | β€” | Main applies the opt-out to every trigger and that is kept, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | +| **SUPB3-O03** | Referral limit: at the limit, a burst can sometimes refuse a send that would have fit; it never over-sends. | β€” | Exactly one winner in every ordering needs a shared row or an RPC, per the body. | πŸ”„ IN PROGRESS β€” owner action: a shared row or RPC, if exact admission at the limit is wanted. | + +## Support batch 2: Family Health medicine readers, removed child's login, provider-sync rotation β€” audit lane, PR #1036 β€” 2026-10-13 + +Result: **3 πŸ›  FIXED + PASS, 1 πŸ”„ IN PROGRESS (owner item).** Three code-only fixes from two Support branches (`member-privacy-7q6vtf` and `membership-lifecycle-7q6vtf`), merged onto main `9e898cf0a`; everything else on those branches was already on main, so the net diff is 6 files. No migration, no SQL, nothing under `supabase/migrations/`; tests are synthetic only (in-memory doubles, no provider calls). Validation on the merged tree `54772a149`: eslint on the changed files exit 0; vitest 43 files, 539 tests all passing, covering the three new tests, every test that imports the changed route, page or `lib/supabase/auth.ts`, and the write, i18n and ordering ratchet and guard tests; `tsc --noEmit -p .` exit 0; the full suite, E2E and the build were left to CI. Merged as `3136816c0`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **SUPB2-01** | The Family Health page asked `isManager`, so a caregiver lost the household's medicine list and, until held 0511 is applied, a teen saw each sibling's and parent's medicine and dose. | β€” | `app/(app)/dashboard/family-health/page.tsx`; `readsEveryMedication` in `lib/medications/readers.ts` (the owner's recorded rule); `tests/the-family-health-page-shows-a-child-only-their-own-prescriptions.test.ts`. | **Fixed:** the page asks `readsEveryMedication`: parents, adults and caregivers read the household's medicines, anyone else only the ones prescribed to them, and a caller with no member row is filtered by the nil UUID, per the body. | +| **SUPB2-02** | A removed child's PIN login with no active family went through `ensureActiveFamily`, which created a new family with the child as its parent. | β€” | `lib/supabase/auth.ts`; `requireUserContext`; `isChildLoginEmail` from main; `tests/a-removed-child-does-not-become-a-parent.test.ts`. | **Fixed:** `requireUserContext` sends a child-login email back to `/kid-login`, which refuses that login, per the body. | +| **SUPB2-03** | The provider-sync cron took the 25 accounts with the oldest `last_synced_at`, and only a success moves that date, so 25 always-failing accounts (revoked grant, unconfigured provider, policy refusal) took every run. | β€” | `app/api/cron/provider-sync/route.ts`; an `app_settings` cursor `provider_sync_cursor:v1`, the pattern push dispatch uses; `tests/a-failing-calendar-account-does-not-starve-the-rest.test.ts`. | **Fixed:** the run walks open families' enabled accounts in id order from the cursor, wraps round at the end, saves the cursor before syncing so an account that throws mid-batch cannot pin the next run, and answers 500 if the cursor read or write fails instead of starting over at the same batch, per the body. | +| **SUPB2-O01** | A child's or guest's read of `medications` returns every member's rows at the database until held 0511 is applied. | β€” | The Family Health fix is the code side of 0511; it closes the page's exposure now and keeps working after 0511 is applied, per the body. | πŸ”„ IN PROGRESS β€” owner action: apply 0511. | + +Per the body: two overlapping provider-sync runs can still share a batch, as the old code could; the cursor key is new in `app_settings`, service-only, with no schema change. + +## AI authority follow-ups and public surfaces β€” audit lane, PR #1028 β€” 2026-10-12 + +Result: **23 πŸ›  FIXED + PASS (the 4 authority items #1016 left in progress plus 4 second-round authority fixes; 10 confirmed public-surface findings, shown in 6 rows as the body groups them; the coordinator's M1 to M5), 4 πŸ”„ IN PROGRESS (owner items).** Two pieces of work built on main `5fbf251c5`: the authority items #1016 left in progress plus A2 from the coordinator's review of #1016, and the public-surfaces audit fix with its follow-up rounds; every round was adversarially reviewed and ended with no open P0 or P1. Every new defect case fails on the source before it and passes after. Validation on the combined head `5328db0f9`: tsc clean, eslint clean on changed files, the i18n gate clean; 74 test files (1,154 tests) pass, covering every changed test and the ratchets; per-round runs in UTC, Los Angeles and Tokyo: AI round 1 251 files (4,147 tests), AI round 2 392 files (7,164 tests), M1 to M5 62 files (977 tests). Merged as `82efd76bd`; no migration, schedule, configuration or environment change; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **AIAUTH-01** | AMT β€” A capped rule such as "Bubaly may spend up to $50" acted as an uncapped allow, so a $900 change ran. | β€” | Commit `295659a24`; the run-time gate and `gateAiAction`; `lib/trust/amount.ts`. | **Fixed:** both gates pass the amount to the trust engine; the amount reader moved into `lib/trust/amount.ts` so the gate can use it without an import cycle; the $900 change is held for approval, per the body. | +| **AIAUTH-02** | EVERYONE β€” An "everyone" rule or a capability-"all" rule let the assistant of a guest, child, caregiver or teen write with the risk tier silent. | β€” | Commit `295659a24`; trust engine. | **Fixed:** no longer; reads still follow what that person may read; a role rule naming that role and exactly "automate" still applies; deny and require-approval rules still bind everyone; a member acting directly is unchanged, per the body. | +| **AIAUTH-03** | PLAN β€” A planted concierge approval row could run a plan. | β€” | Commit `295659a24`. | **Fixed:** a plan runs only when Bubaly filed the approval row; a planted row writes nothing, per the body. | +| **AIAUTH-04** | A2 β€” The final update in `completeChoreAssignment` was not pinned for anyone but a parent, adult or the system. | β€” | Commit `295659a24`. | **Fixed:** pinned to the member's own row and to a row not yet approved, per the body. | +| **AIAUTH-05** | A planted row could govern the Autopilot panel's approve button (a regression from `295659a24`). | β€” | Commit `2f9e2d6c6`. | **Fixed:** no longer, per the body. | +| **AIAUTH-06** | An approver's edit could move a card under a stricter or different rule, so one parent could raise $40 to $900 past a two-parent money rule. | β€” | Commit `2f9e2d6c6`; `editAndApprove`, `decide()` and the run executor. | **Fixed:** refused when the edit would move the card under a deny, another policy, or a stricter model or count, per the body. | +| **AIAUTH-07** | A concierge approval materialized a plan that had changed since it was accepted. | β€” | Commit `2f9e2d6c6`. | **Fixed:** the approval stores a snapshot of the plan fields that become records and refuses to materialize if the live plan changed; rows filed before this change are refused and the family accepts the plan again, per the body. | +| **AIAUTH-08** | A held step's approval rule read an amount other than the one it would run with. | β€” | Commit `2f9e2d6c6`; the tool's schema. | **Fixed:** reads the amount with bindings resolved through the tool's schema; an unreadable amount never matches a capped allow, per the body. | +| **AIAUTH-09** | Public forms β€” automation mail from public contact and form submissions was unbounded and the greeting carried whatever name was typed. | β€” | Commits `3fe5b67c8`, `a107df276`. | **Fixed:** one mail per inbox per day, by canonical inbox; the greeting carries at most a plain three-word name, per the body. | +| **AIAUTH-10** | Opt-outs β€” automation mail ignored `marketing_suppressions`, carried no one-click unsubscribe, and an Unsubscribe-link click started an `email_clicked` workflow. | β€” | Commits `3fe5b67c8`, `a107df276`. | **Fixed:** honours `marketing_suppressions` and fails closed when that read errors; each mail carries a signed one-click unsubscribe with List-Unsubscribe headers; an unsubscribe click never starts the workflow, per the body. | +| **AIAUTH-11** | Blog subscribe β€” the throttle was not keyed on the canonical inbox and a submission silently reversed an unsubscribe. | β€” | Commits `3fe5b67c8`, `a107df276`. | **Fixed:** keyed on the canonical inbox; no silent reversal, per the body. | +| **AIAUTH-12** | `rate_limits` stored raw IPs and email addresses and was never pruned. | β€” | Commits `3fe5b67c8`, `a107df276`. | **Fixed:** stores keyed digests and is pruned daily, per the body. | +| **AIAUTH-13** | Exit intent ignored a visitor's personalisation choice. | β€” | Commits `3fe5b67c8`, `a107df276`. | **Fixed:** respects it, per the body. | +| **AIAUTH-14** | A failed services read answered an empty set cached at the edge. | β€” | Commits `3fe5b67c8`, `a107df276`. | **Fixed:** answers 503 with no-store, per the body. | +| **AIAUTH-15** | M1 β€” A blog opt-out could be reversed by a notice the provider had not accepted, and an unsubscribe landing mid-request could be overwritten. | β€” | Commits `fe9620b5c`, `ffa2823a8`. | **Fixed:** reversed only by an accepted notice; the write is guarded on the observed status, per the body. | +| **AIAUTH-16** | M2 β€” An alias unsubscribe did not hold for the whole inbox. | β€” | Commits `fe9620b5c`, `ffa2823a8`. | **Fixed:** the canonical inbox row is recorded; alias-only rows written before still suppress, per the body. | +| **AIAUTH-17** | M3 β€” The one-mail allowance was not enforced at each send. | β€” | Commits `fe9620b5c`, `ffa2823a8`. | **Fixed:** enforced at each send, across workflows and steps, per the body. | +| **AIAUTH-18** | M4 β€” Exit offers ignored consent withdrawn on the same page and treated an unknown visit history as known. | β€” | Commits `fe9620b5c`, `ffa2823a8`. | **Fixed:** follow withdrawn consent and treat unknown history as unknown, per the body. | +| **AIAUTH-19** | M5 β€” The rate-limit digest could run on a public constant in production. | β€” | Commits `fe9620b5c`, `ffa2823a8`. | **Fixed:** refuses a public constant in production, per the body. | +| **AIAUTH-O01** | `concierge_plans`: 0091 lets any active member update any plan. | β€” | The app now refuses to materialize a changed plan; SQL not written, per the body. | πŸ”„ IN PROGRESS β€” owner action: restrict update and delete to managers or the plan's creator. | +| **AIAUTH-O02** | Existing `rate_limits` rows keep raw keys until the first daily prune. | β€” | The body gives an optional `delete from public.rate_limits where bucket_key ~ …` statement. | πŸ”„ IN PROGRESS β€” owner action (optional): run the delete now. | +| **AIAUTH-O03** | Judgement call: a role rule naming a teen, or another role not trusted for automation, with exactly "automate" still extends the assistant's writes. | β€” | Left as is, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | +| **AIAUTH-O04** | Not done: double opt-in for public forms, and a per-field length cap on stored form submissions. | β€” | Each needs new copy and UI, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + +Known minor notes (P3), per the body: the edit check reads the approval row's `confidence`, a column the approver can write, so with a `minConfidence` allow an edit could still be weighed as confident; with a very large suppression table the 500-row LIKE read fails closed for some Gmail addresses; campaigns (`lib/marketing/send.ts`) still match exact spellings plus the canonical rows written from now on; if consent is withdrawn and the exit gesture never happens, the visit marker stays until the next mount. + +## Support batch 1: referral credit once, deleted and archived data, child wallet ownership, server-action census β€” audit lane, PR #1022 β€” 2026-10-12 + +Result: **11 πŸ›  FIXED + PASS (the fixes the body lists across four Support branches), 0 πŸ”„ IN PROGRESS (owner items; the body defers schema-change leftovers to #771 without listing them).** Four Support branches combined into one PR, each merged with `--no-ff`; code and tests only, no migrations; each fix's claim and completion is on #771. Each fix was failing-first against its base and its mutants were killed. Validation on the branch's tree (main `eb58aca93` plus the four branches): eslint on the changed files clean; full tsc clean; full vitest 2,166 files passed and 3 skipped, 32,413 tests passing, 2 expected failures and 95 skipped; the one failing file in that run was the invest test, whose expected error had not been staged in the reconciling merge, fixed in `719c71402` (15 of 15 pass) and merged, and the suite was not re-run after that; the branch merges cleanly with main `c758fb427`. Merged as `06d89d3ff`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **SUPB1-01** | Referrals β€” a retried or replayed reward credited the referral reward again. | β€” | Support branch `referral-credit-once-7q6vtf`; `rewardReferral`. | **Fixed:** before writing a credit, `rewardReferral` checks Stripe for an existing reward credit on each customer, per the body. | +| **SUPB1-02** | Referrals β€” the reward used `getStripe()`, which read only `STRIPE_SECRET_KEY`. | β€” | Same branch; the billing routes' key resolution. | **Fixed:** uses the Stripe key the billing routes use (Stripe Setup first, then the environment), per the body. | +| **SUPB1-03** | Deleting a home asset deleted its database rows but not its files, so a warranty moved into the Secure Vault became readable by every member once its appliance was deleted. | β€” | Support branch `data-lifecycle-7q6vtf`; storage. | **Fixed:** deleting a home asset deletes its files from storage, per the body. | +| **SUPB1-04** | A deleted warranty still appeared in the morning brief and the Briefing page. | β€” | Same branch. | **Fixed:** no longer, per the body. | +| **SUPB1-05** | The dashboard's unread count included deleted messages and the reader's own. | β€” | Same branch. | **Fixed:** skips both, per the body. | +| **SUPB1-06** | Editing a deleted contractor, warranty or Auto record was reported as saved. | β€” | Same branch. | **Fixed:** refused, per the body. | +| **SUPB1-07** | A service visit could name a deleted contractor or another family's contractor. | β€” | Same branch. | **Fixed:** refused, per the body. | +| **SUPB1-08** | Items on an archived shopping list produced "Still need" Autopilot suggestions and appeared in the daily and weekly briefings. | β€” | Same branch. | **Fixed:** left out of both, per the body. | +| **SUPB1-09** | Child wallet β€” the Invest page showed Buy, Sell and Request on cards the viewer may not order on. | β€” | Support branch `child-wallet-authorization-7q6vtf`; the server-side check itself came in with #989, and this branch's test pins main's answer. | **Fixed:** the buttons show only on cards the viewer may order on, per the body. | +| **SUPB1-10** | Child wallet β€” a parent in two households saving from a stale tab created an allowance that never paid. | β€” | Same branch. | **Fixed:** a new allowance or goal must be for a child wallet in the active family, per the body. | +| **SUPB1-11** | SRV-001 census β€” three `'use server'` files (knowledge, agents and vacations) were named by no test. | β€” | Support branch `srv001-untested-actions-7q6vtf`; `tests/the-last-unnamed-server-actions.test.ts`; this branch's own flight-delay fix was superseded by main's `reportTripDisruption` rewrite and dropped. | **Fixed:** every `'use server'` file is now named by a test, per the body. | + +Not included, per the body: `billing-one-subscription-7q6vtf` is superseded by #989's billing fix; entitlement, membership, sending-crons, marketplace and member-privacy were reconciled with #989 and came as later batches (#1036 and #1037 above); no database migration, production setting or provider change; leftover issues that need schema changes are listed for the owner on #771. + +## AI tool registry β€” audit lane, PR #1016 β€” 2026-10-12 + +Result: **26 πŸ›  FIXED + PASS, 3 πŸ”„ IN PROGRESS (owner items).** Fixes the 26 confirmed findings of the finalaudit AI tool registry audit (two critical, nine high), in three groups: who Bubaly acts for, what its reads show each role, and how it reads times. The body as merged still carries a "Status: draft" note saying a fourth review round was running on the merged head and would close three gaps the earlier rounds left open (amount caps at the run-time gates, everyone-subject allows for a restricted requester, planted concierge plans); those three were closed by #1028 (AIAUTH above), whose body says so. The branch was merged with main after #1004, with one conflict in the `search.household` description resolved with the branch's line; #1004's gate for restricted-role sensitive reads, `readSkipsTrustGate` and the planner dry run merged cleanly. Validation: not stated in the PR beyond "No migration, schedule or configuration change". Merged as `b74e44b2f`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **AITOOL-01** | AI5, AI10, AI15 β€” Outside senders: inbound email, voicemail and unknown senders could have Bubaly act, and an SMS was not tied to a verified member. | Critical | SMS planning; inbound email and voicemail; the inbox "Handle it" action. | **Fixed:** only an SMS from a member's confirmed login phone is planned, as that member's own request; email, voicemail and unknown senders are filed for a parent to read (`unverified_sender`); Handle it plans fenced text and every write it produces is held for approval, per the body. | +| **AITOOL-02** | AI4 β€” Planted approvals: an approval row a member wrote themselves could run on approval. | β€” | Approval cards. | **Fixed:** a card Bubaly filed replays exactly what was filed; a member-written row runs nothing; a row the server's gate filed for a member replays as filed, per the body. | +| **AITOOL-03** | AI6 β€” Steps the planner held were not filed under the family's approval model. | β€” | Planner; approval model. | **Fixed:** filed under the family's model so two-parent and consensus rules hold; the amount is evaluated the same way at plan time and at run time, per the body. | +| **AITOOL-04** | AI7, AI12 β€” Learned Autopilot allows and the Autopilot dial could give an assistant more than its requester may do. | β€” | Autopilot policies. | **Fixed:** they never can, per the body. | +| **AITOOL-05** | AI11, AI16 β€” Notify steps bypassed the trust gate. | β€” | Notify steps. | **Fixed:** go through the trust gate for the requester's role, per the body. | +| **AITOOL-06** | AI8 β€” A teen could settle a sibling's chore. | β€” | Chore settlement. | **Fixed:** refused, per the body. | +| **AITOOL-07** | AI13 β€” Edited inputs could speak as another member in family messages, RSVPs and announcements. | β€” | Family messages, RSVPs, announcements. | **Fixed:** never, per the body. | +| **AITOOL-08** | AI14 β€” Magic Import approvals did not carry their requester. | β€” | Magic Import. | **Fixed:** they carry it, per the body. | +| **AITOOL-09** | AI17 β€” Tool results reached the re-plan prompt unfenced. | β€” | Re-plan prompt. | **Fixed:** fenced, per the body. | +| **AITOOL-10** | AI20 β€” "Waiting for a parent" was shown without a card. | β€” | Approval status. | **Fixed:** only when a card exists, per the body. | +| **AITOOL-11** | AI26 β€” A tool's natural idempotency key was not kept. | β€” | Tool execution. | **Fixed:** kept, per the body. | +| **AITOOL-12** | AI1 β€” `search.household` showed trips and vacations to guests and children, and documents to roles the documents slice refuses. | β€” | `search.household`. | **Fixed:** withholds them, per the body. | +| **AITOOL-13** | AI2 β€” The toolbox's pending decisions showed money amounts and renewals to non-managers. | β€” | Toolbox pending decisions. | **Fixed:** hidden from non-managers, per the body. | +| **AITOOL-14** | AI3 β€” The proactive context slice put children's school, homework and sports detail in a guest's prompt. | β€” | Proactive context slice. | **Fixed:** kept out, per the body. | +| **AITOOL-15** | AI9 β€” Chat's announcement tool was not gated under messaging. | β€” | Chat announcement tool. | **Fixed:** gated, per the body. | +| **AITOOL-16** | AI18 β€” Chat toolbox writes resolved offset-less times outside the family zone. | β€” | Chat toolbox; `calendar.createEvent` and its pinned test. | **Fixed:** resolved in the family zone; the all-day rewrite happens only on the chat path, before the approval card is filed, so `calendar.createEvent` stays as strict as its pinned test, per the body. | +| **AITOOL-17** | AI19 β€” Recurring reminders and chores did not roll on the family calendar across DST. | β€” | Recurrence. | **Fixed:** they do, per the body. | +| **AITOOL-18** | AI22 β€” Snooze and list-due read times off the family clock. | β€” | Snooze; list-due. | **Fixed:** on the family clock, per the body. | +| **AITOOL-19** | AI23 β€” Notify sent at the wrong time and ignored quiet hours. | β€” | Notify. | **Fixed:** sends at the family's time and keeps quiet hours, per the body. | +| **AITOOL-20** | AI24 β€” Chore and maintenance due dates did not use the family's midnight; the family dashboard's "Due today" counted the wrong range. | β€” | Chores; maintenance; family dashboard. | **Fixed:** the family's midnight; "Due today" counts by the family-day range, per the body. | +| **AITOOL-21** | AI21 β€” Finance totals were computed from a truncated read. | β€” | Finance totals. | **Fixed:** page through every row or fail loudly, per the body. | +| **AITOOL-22** | AI25 β€” The inventory search answered "nothing matches" from a truncated list. | β€” | Inventory search. | **Fixed:** no longer, per the body. | +| **AITOOL-O01** | Recurrences in the spring-forward gap keep the resolved time. | β€” | Keeping the original wall time needs a new column, per the body. | πŸ”„ IN PROGRESS β€” owner action: the column. | +| **AITOOL-O02** | Rows already stored at UTC midnight are not migrated. | β€” | Data migration, per the body. | πŸ”„ IN PROGRESS β€” owner action: migrate existing rows. | +| **AITOOL-O03** | Finance totals stop at 5,000 rows per window and inventory at 10,000; past those the call fails visibly. | β€” | Raising them is a product call, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + +Left as is, per the body: a parent can no longer reword a child's held family message or RSVP; they can approve or decline it as written. + +## Guardian recovery β€” audit lane, PR #1013 β€” 2026-10-11 + +Result: **2 πŸ›  FIXED + PASS, 0 πŸ”„ IN PROGRESS (owner items).** Guardian recovery follow-ups after #1004 merged, on main `c95b1180d`. Three coordinator items were each checked by two independent skeptics; two were confirmed and fixed in a worktree with a multi-tick test that drives the real cron GET, drain, sweep and landing on a fake clock, and adversarially reviewed with no blockers; the third (a corrupted retry mark plus a renamed contact alerting twice, item 6106521778) was refuted by both skeptics, the coordinator withdrew the ordinary-path reading, and nothing changed. The body states, not claims: a count that does not land is not advancement; that item keeps its place, the items behind it are counted deferred, and the tick answers 503; the 22 s reserve holds when the receipt drain ends inside its 55 s deadline. Validation per the body: the recovery route, owed landing and retry sweep tests and the repo ratchets pass in three time zones; `eslint --no-ignore` clean on the changed files; CI green on `cca4b0298`, E2E included. Merged as `d75264fbb`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **GREC-01** | An owed landing that stalled until the deadline was never counted and held every tick (coordinator item 6106521778). | β€” | Confirmed under the PR's own stalled-statement fault model; a multi-tick test on a fake clock drives the real cron GET, drain, sweep and landing (file not named in the PR). | **Fixed:** the owed phase gets at most half of what is left of the run; each landing is cut off after 10 s; a cut-off landing counts as a try, per the body. | +| **GREC-02** | Counting a failed landing could take the ordinary retries' turn (coordinator item 6107156295): with a 55 s drain the retries were left 13.5 s, below their 20 s threshold. | β€” | Same test approach. | **Fixed:** every count write is bounded by the owed phase's end; ordinary retries always keep 22 s; the overall deadline is unchanged, per the body. | + +The body lists no owner items; it notes that the AI tool registry fix (26 confirmed findings) lands as its own PR (#1016, AITOOL above). + +## Audit follow-ups: Guardian; trips, school and sports; household modules β€” audit lane, PR #1004 β€” 2026-10-11 + +Result: **55 πŸ›  FIXED + PASS (22 findings plus 12 from the review lenses in trips, school and sports; 21 in the household modules) plus the Guardian follow-ups in five areas (count not stated in the PR), 8 πŸ”„ IN PROGRESS (owner items).** Three audit lanes integrated onto main after #989: the Guardian follow-ups raised on #989's thread, the trips/school/sports audit and the household-modules audit. Every finding was kept only where two independent skeptics could not refute it, fixed in a worktree with tests that drive the real webhook, route, action or cron and fail without the fix, and adversarially re-reviewed until no blocker remained; the coordinator's eight items on this PR went through the same process and all eight held and are closed or carry a stated limit. Validation per the body: each lane passed tsc, `eslint --no-ignore`, `npm run i18n:gate` and its related tests in UTC, America/Los_Angeles and Asia/Tokyo plus negative controls; on the merged head tsc exit 0 and 83 test files (every changed test plus the repo's ratchets), 1,446 tests; CI runs the full suite. Merged as `c95b1180d`; no migrations, schedules or production settings changed; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **FOLLOW1-01** | Guardian, emergency delivery β€” an escalation that reached nobody was not retried; a family with no reachable manager could be retried forever; a record that failed to land was lost. | β€” | The existing recovery cron. | **Fixed:** a bounded sweep retries it; an unreachable family is recorded once as unreachable; a record that fails to land leaves a work item the sweep lands first, per the body. | +| **FOLLOW1-02** | Guardian, one alarm per emergency β€” a late WhatsApp redelivery could raise a second alarm; a sweep that reached managers but could not record it could re-send. | β€” | WhatsApp inbound; escalation keys. | **Fixed:** a redelivery keeps its original escalation key (communication id, or a mark keyed on its own message SID); a separate message with the same words is its own emergency; the sweep does not re-send, per the body. | +| **FOLLOW1-03** | Guardian, deadline and fairness β€” the recovery cron had no shared budget, could start attempts it could not finish, and an old failure could starve a new emergency. | β€” | Recovery cron; Twilio requests. | **Fixed:** one 100 s budget shared across the receipt drain and the sweep; never starts an attempt it cannot finish; reports what it deferred; rotates through records; cancellation reaches every Twilio request; a late retry says it is late and when the emergency was first raised, per the body. | +| **FOLLOW1-04** | Guardian, model cap and membership β€” an unreadable count bought a model call; inbound lanes did not re-check membership. | β€” | WhatsApp lane; every inbound lane; screening lookup. | **Fixed:** WhatsApp records, then counts; every inbound lane and the screening lookup re-check active membership, per the body. | +| **FOLLOW1-05** | Guardian, removal β€” a member's location might silently fail to clear on removal. | β€” | Every removal surface; the held retention sweep. | **Fixed:** every removal surface warns when the location could not be cleared; the removed-member location repair is family-scoped and stays inside the held retention sweep, which this PR does not schedule or activate, per the body. | +| **FOLLOW1-06** | Trips, school and sports β€” civil time: times were not stored on a consistent clock and folds, gaps and impossible dates were not handled one way. | β€” | Trip, school, sports and reminder times; RFC 5545 Β§3.3.5. | **Fixed:** one rule everywhere; stored on the family's or the trip's clock; folds and gaps resolve one documented way, midnight-gap zones included; impossible dates refused; trip zones stored by IANA name, per the body. | +| **FOLLOW1-07** | Disruptions β€” any member could report one, repeats duplicated, delivery was tied to the re-flow, and rollback could cascade away a restored item. | β€” | Disruption report and delivery. | **Fixed:** a manager's act; a repeat returns the original receipt; delivery is tracked apart and retried alone; rollback never cascades away a restored item, per the body. | +| **FOLLOW1-08** | Trip AI β€” a rebuild could delete a row booked or edited during generation; a concierge turn was stored before a reply; weather ran outside the trip's dates. | β€” | Trip rebuild; `updated_at` fence. | **Fixed:** the rebuild claims the trip and fences every delete at write time (booked, the builder's marker, the row's own `updated_at`); a concierge turn is stored only after a reply; weather stays inside the trip's dates, per the body. | +| **FOLLOW1-09** | Trust β€” read-only travel, school and sports tools, search and insights did not apply the private-read rule for restricted roles; the planner's dry run disagreed with the run; Magic Import could do more than it proposed. | β€” | Tools, search, insights; planner dry run; Magic Import. | **Fixed:** the same private-read rule everywhere; the dry run agrees with the run; Magic Import carries out only what it proposed, per the body. | +| **FOLLOW1-10** | Household β€” AI Assist and the Family Chef answered from failed reads. | β€” | AI Assist; Family Chef. | **Fixed:** no longer, per the body. | +| **FOLLOW1-11** | Household β€” closet and inventory photos stayed in the public bucket after their item was deleted. | β€” | Closet; inventory; public bucket. | **Fixed:** they leave with their item, per the body. | +| **FOLLOW1-12** | Household β€” the meal and recipe AI endpoints lacked the plan gate and did not fence household text; the AI meal planner served guests. | β€” | Meal and recipe AI endpoints; AI meal planner. | **Fixed:** carry the plan gate and fence household text; the planner refuses guests like the manual actions, per the body. | +| **FOLLOW1-13** | Household β€” confirmed deletes did not leave the screen. | β€” | Household modules. | **Fixed:** they leave the screen, per the body. | +| **FOLLOW1-14** | Household β€” closet wear, declutter, nutrition, sleep and recipe counts were written more than once or off the family's day. | β€” | Those counters. | **Fixed:** written once and on the family's day, per the body. | +| **FOLLOW1-O01** | SQL β€” `guardian_escalations.source_event_id` with a unique index. | β€” | Listed under stated limits and owner items, per the body. | πŸ”„ IN PROGRESS β€” owner action: the column and index. | +| **FOLLOW1-O02** | SQL β€” a trip build lease or transactional replace. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action. | +| **FOLLOW1-O03** | SQL β€” a unique index on disruption notices. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action. | +| **FOLLOW1-O04** | SQL β€” a transactional disruption report. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action. | +| **FOLLOW1-O05** | SQL β€” same-family composite keys on nine member reference columns. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action. | +| **FOLLOW1-O06** | SQL β€” the `vacations.timezone` backfill. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action. | +| **FOLLOW1-O07** | Schedule β€” `guardian-sms-recovery` is guaranteed one daily run on the current plan. | β€” | A five-minute cadence needs a reliable dispatcher or Vercel Pro, per the body. | πŸ”„ IN PROGRESS β€” owner action: choose the cadence and the dispatcher or plan. | +| **FOLLOW1-O08** | Owner decision β€” the location-history retention window. | β€” | Needed before the held retention sweep (and the repair inside it) can run, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + +## Audit fixes, 16 areas β€” audit lane, PR #989 β€” 2026-10-10 + +Result: **17 areas πŸ›  FIXED + PASS (the title says 16 areas and the body's table lists 17; the number of individual findings is not stated in the PR), 5 πŸ”„ IN PROGRESS (owner items).** Integrates the audit fix branches produced by the finalaudit.md audit programme, each the result of a five-lens find, two-skeptic verify, worktree fix and independent adversarial review, with re-fix rounds where the reviewer blocked; base is main through #995 and #991. Migrations 0477–0485 are in the PR, assembled from the audit's tested proposals and verified on a fresh PG16 replay (460 migrations, 188 of 188 probes, idempotent second apply, ledger audit next 0486, typecheck clean, CI's Database job green); nothing is applied by merging. Validation per the body: each branch passed tsc, `eslint --no-ignore` and its related vitest files in UTC, America/Los_Angeles and Asia/Tokyo before integration; the integrated head `8bcf71223` passed the full vitest suite locally in UTC and the carve-out commit re-ran it (2,087 files; only the two Node-22-only checks fail locally); after each later merge the merge-sensitive files were re-run; CI on the previous head had E2E, Database replay and every other job green except the four held-lane controls and three unit tests, all addressed in `113eb3b90`. Merged as `209e5a48b`; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **AUD16-01** | X OAuth and scheduled posting β€” state and token binding, connection ownership. | Medium | Area row in the body's table. | **Fixed:** state/token binding and connection ownership, per the body. | +| **AUD16-02** | Alexa assistant keys β€” key ownership and rotation. | Medium | Area row; migration 0477. | **Fixed:** key ownership and rotation, per the body. | +| **AUD16-03** | Finance and goal deletes were not role-checked or confirmed. | Medium | Area row; migration 0478. | **Fixed:** role-checked, confirmed deletes, per the body. | +| **AUD16-04** | Reminder and to-do deletes were not role-checked, and reassign-then-delete bypassed the check. | Medium | Area row; migration 0479. | **Fixed:** role-checked deletes; the bypass is closed, per the body. | +| **AUD16-05** | AI approvals β€” dedupe-key forgery, the approval PIN flow, parents-only paths. | High | Area row; migration 0480. | **Fixed:** per the body. | +| **AUD16-06** | Money writes β€” savings-goal and transaction-intent correctness; budget duplicate cleanup. | Medium | Area row. | **Fixed:** the cleanup lands the requested amount on the canonical row, per the body. | +| **AUD16-07** | AI memory and privacy β€” sensitive-term check, per-member memory scope, the recall slice. | High | Area row. | **Fixed:** sensitive-term check, per-member scope, fenced recall slice, per the body. | +| **AUD16-08** | Inbound email/SMS and urgent texts β€” the SMS budget counted attempts; sender verification. | Medium | Area row. | **Fixed:** the budget counts sends not attempts; sender verification, per the body. | +| **AUD16-09** | Notifications and push β€” marketing push to minors; per-user reads; push detach auth. | High | Area row; migration 0485. | **Fixed:** no marketing push to minors; per-user reads; push detach auth, per the body. | +| **AUD16-10** | Documents and paperwork β€” `member_id` family check; sensitive titles on the feed; expiry day; guest capture; OCR failure handling. | Medium | Area row; migration 0484. | **Fixed:** family check; sensitive titles off the feed; family-day expiry; guest capture refused; permanent OCR failures skipped and config failures retried, per the body. | +| **AUD16-11** | Autopilot and purchases β€” accept races; refresh fencing; purchase-state leak; capped prior reads; reminder times; cron rotation. | Medium | Area row. | **Fixed:** compare-and-set accept; fenced refresh; no purchase-state leak; paged prior reads; deliverable reminder times; fair cron rotation, per the body. | +| **AUD16-12** | Account close, crons, import β€” closing did not cancel Stripe billing; family crons ran for closed families; import idempotency and assignment. | Medium | Area row. | **Fixed:** closing cancels Stripe billing (reopen resumes); every family cron skips closed families; import idempotency and assignment repair, per the body. | +| **AUD16-13** | Family membership β€” adults could assign or remove parents; removed child logins; removed members' sync; onboarding invite cap and resend; adopt takeover. | High | Area row; migration 0482; #981's kid-identity and PIN-reset provenance guards preserved. | **Fixed:** adults cannot assign/remove parents; removed child logins banned and restored on re-add; removed members' sync refused for every caller; invite cap and resend; adopt takeover closed, per the body. | +| **AUD16-14** | Billing and entitlements β€” double subscriptions; billing routes without AAL2; client-side trial lock; past_due handling; unknown-price cancellations; duplicate webhook rows. | High | Area row. | **Fixed:** no double subscriptions; AAL2 on billing routes; server-side trial lock; 14-day past_due grace; unknown-price cancellations land; webhook tolerates duplicate rows, per the body. | +| **AUD16-15** | Family economy β€” minors dealing with other households; pickup completion; invest orders; gift messages; auction notices; order and circle reads. | High | Area row; migration 0483. | **Fixed:** minors cannot deal with other households; party-only pickup completion; invest orders only on own wallet; gift messages held for approval; auction-notice retry; order reads via `buyer_family_id` with fallback; circle reads via view with fallback, per the body. | +| **AUD16-16** | Super-admin console β€” no step-up; admins could ban admins; Stripe secrets overwritten with null; roster revoke; unaudited config changes; dropped audit ids. | Medium | Area row. | **Fixed:** AAL2 step-up on every admin surface; admins cannot ban admins; secrets never overwritten with null; roster revoke really revokes; all config changes audited; non-uuid audit ids no longer dropped, per the body. | +| **AUD16-17** | Child safety: Guardian, locator, emergency β€” emergency escalation was computed and never read; quiet hours and curfews ran on New York time; emergency texts waited for quiet hours; blocked senders could ring through on keywords; a Guardian profile could name another family's member; disabled geofences fired; removed members kept their location; trusted contacts were held by the inbound cap. | High | Area row. | **Fixed:** escalation fires; quiet hours and curfews on the family's timezone; emergency texts escalate; blocked senders cannot ring through; a profile cannot name another family's member and the voice route refuses one; disabled geofences stay silent; removed members take their location with them; trusted contacts are never held by the inbound cap while the model-based scam call stays bounded, per the body. | +| **AUD16-18** | Integration repairs. | β€” | Unconfirmed writes; fixture mocks; the recall slice; copy; e2e fixture source lists; the marketplace rail's Trust Score; notification-email and sign-out fixture loaders; every member-removal path; merges with #991 and the guardian branch. | **Fixed:** writes confirmed; mocks for the merged admin step-up, open-family reads and the ban marker; recall slice fenced and registered; new copy in the catalogues; the Trust Score reads through a server action; the loaders resolve the merged modules (the module-graph guard now catches both drifts); every removal path does the membership steps and the location forget, per the body. | +| **AUD16-O01** | Migrations 0477–0485 are in the repository but not applied to production. | β€” | The production-migrations workflow only verifies on main; applying needs the manual `apply=true` dispatch after the rejected service-role key is replaced; the app halves in this PR must be deployed before 0483 (its circle-feed view replaces the dropped read policy); `docs/PENDING_PROD_MIGRATIONS.md` records this; 0482's last-parent rule also binds the service role; 0483 narrows `gift_payments` reads for children, per the body. | πŸ”„ IN PROGRESS β€” owner action: replace the service-role key, then apply 0477–0485 in the recorded order. | +| **AUD16-O02** | The `accept_invite` rewrite is deferred to the held 0495; the inviter-still-active re-check follows once 0495 lands. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: land 0495, then the re-check. | +| **AUD16-O03** | `kid_progress`, the `calendar_feeds` guest guard and the reward request shape guard are deferred to the held 0496, 0498 and 0500. | β€” | Their negative-control workflows must keep seeing those defects until the held files land, per the body. | πŸ”„ IN PROGRESS β€” owner action: land 0496, 0498 and 0500. | +| **AUD16-O04** | Composite foreign keys for the Guardian tables (`guardian_member_profiles`, `guardian_contacts`, `guardian_routing_rules`). | β€” | Recorded as a proposal for the owner, per the body. | πŸ”„ IN PROGRESS β€” owner action. | +| **AUD16-O05** | A database-side location-forget trigger. | β€” | Recorded as a proposal for the owner, per the body. | πŸ”„ IN PROGRESS β€” owner action. | + +Per the body, the sync mapping-CAS to item-touch repair from #982's thread landed separately as #992. + +## Production schema fallbacks β€” audit lane, PR #982 β€” 2026-10-10 + +Result: **5 πŸ›  FIXED + PASS (five areas that fall back to previous production behaviour while their migration is absent), 3 πŸ”„ IN PROGRESS (owner items).** A read-only, adversarially verified review found that main (`6d9d9207f` and later, with #969), live in production, depended on database objects production lacked (0475 and 0476 never applied; 0488, 0490, 0493 and 0494 held in `supabase/reserved/`): every family saw an empty Messages inbox, Google, Outlook and iCloud sync stopped, calendar subscriptions neither refreshed nor could be added, Free-plan families lost the assistant, and bills due on the 28th–31st could not be added or paid. Per the owner's decision each path falls back to previous production behaviour (`82f2db1fc`) while its migration is absent and uses the new path unchanged once the object exists; absence is detected only from the exact missing-object answer naming the object (PGRST202/42883, PGRST204/42703, PGRST205/42P01), every other error still surfaces, and each fallback warns once per process naming its migration. Tests that asserted the reversed "refuse until the migration lands" decision now expect the fallback, with a comment, while non-missing errors are still asserted to fail. Validation per the body: tsc clean; eslint on changed files clean; `audit-migration-versions` next 0477; full vitest in UTC 30,529 passed (only the two Node-22-only tests fail locally; an `ordering-guards` failure found in that run was fixed in `430041162`); each area's tests pass in UTC, Los Angeles and Tokyo; each area was implemented in isolation and adversarially reviewed; the messaging browser specs `family-messages-state` and `messaging-preserve-access` 79 passed. Merged as `0039e3d23`; nothing under `supabase/migrations` or `supabase/reserved` changed and no migration was applied; live verification is not stated in the PR. + +| ID | Finding | Severity | Evidence | Status | +|---|---|---|---|---| +| **SCHFB-01** | Messaging (web and the assistant tool) depended on the 0475/0476 RPCs and `is_family_chat`, so every family saw an empty inbox. | β€” | Family chat ensure, create, send, duplicate probe, overview, mark-read (`mark_conversation_read`, 0163), reactions and presence. | **Fixed:** while the objects are absent the inbox lists every conversation as before and those paths use the previous code, per the body. | +| **SCHFB-02** | AI usage depended on the held 0493 `count_family_ai_requests_month`, so Free-plan families lost the assistant. | β€” | AI request count. | **Fixed:** a direct `ai_requests` count as before, so Free keeps 10 requests a month, per the body. | +| **SCHFB-03** | Provider sync depended on the held 0494 `ensure_sync_pull_container` and `create_sync_pull_item`, so Google, Outlook and iCloud sync stopped. | β€” | Sync pull container and item writes. | **Fixed:** direct container and item writes as before, per the body. | +| **SCHFB-04** | Calendar feeds depended on the held 0490 `calendar_feed_apply_sync`, so subscriptions neither refreshed nor could be added. | β€” | Calendar feed sync. | **Fixed:** fenced direct writes as before; adding a feed no longer rolls back and no internal path is shown to users, per the body. | +| **SCHFB-05** | Bills depended on the held 0488 `bills.due_day`, so bills due on the 28th–31st could not be added or paid and the forecast failed. | β€” | Bills add, pay and forecast. | **Fixed:** such bills can be added and paid (a clamp asks first, as before) and the forecast loads, per the body. | +| **SCHFB-O01** | Migrations 0475 and 0476 were never applied to production. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: apply 0475 and 0476; the new messaging paths are used unchanged once they exist. | +| **SCHFB-O02** | Migrations 0488, 0490, 0493 and 0494 are held in `supabase/reserved/`. | β€” | Per the body. | πŸ”„ IN PROGRESS β€” owner action: release and apply them; each fallback names its migration when it warns. | +| **SCHFB-O03** | #969's stricter ICS parsing and recurrence admission still reject many feeds previous production imported (VTIMEZONE feeds, Windows zone names, school or sports series). | β€” | Not changed; owner decision pending, per the body. | πŸ”„ IN PROGRESS β€” owner decision. | + + ## Layouts β€” the 29 LAYOUT rows β€” 2026-09-29 *Claude session_01776xJyhVe8xJQvTGeNfT9T. Read on main at `7561ae14`.*