Skip to main content

Persona-lens gap review — feature + architecture docs

Status: Review note (gaps + open questions). Not a decision; feeds the next round of sign-off. Date: 2026-06-25 Reviewed: app/test-gallery/authored/developer/architecture/ — the persona-load-bearing feature docs (personas, roles-permissions, member-profile, household, money, admin, dashboards, account-profile-management, onboarding, notifications, childrens-privacy-coppa, profile-switching, kid-mode-pin-lock, activity-gating, catalog, today-chores) plus the C4 model (index, context, auth, code, dynamics) and a skim of the ~30 remaining stubs. Lens: the four composable personas — Member (base: wallet/buckets/goals/budget), Helper (MemberRole.helper, approve/deny completed jobs only), Admin (MemberKind.isParental + admin, all config), Owner (owner flag, billing) — and the composition rules (grandparent = Member+Helper; parent = Member+Admin+Owner).

The personas are composable overlays, but the docs slide between two readings of each column — "the overlay's isolated grant" vs "a real person who holds that overlay." Most of the apparent contradictions below trace back to that unstated ambiguity. Pinning the matrix semantics is the single highest-leverage fix.


1. Consolidated capability matrix

Columns are read as effective capability of a person whose top overlay is that column (Member = base only; Helper = a person holding the helper overlay; Admin = Member + isParental + admin; Owner = Admin + owner). allow, deny, unresolved/contradicted in the docs — the cells are the gaps.

Action surfaceMemberHelperAdminOwnerSource / note
Do / claim chorespersonas matrix says Helper ; prose says grandparent = Member+Helper (a Member does chores). Depends on "does Helper imply a Member profile" (D2)
Submit a job (complete → submit)same root as above
Approve / deny a job (completion)admin.md widens completion gate to "parental OR helper"; not reflected in dynamics.md flow 1 or code.md Approval.resolvedBy (C-B)
Approve / deny a redemption (spend)money.md keeps spend parental; no row for this in the personas matrix — make explicit that Helper ≠ redemption approver
Move own fundspersonas matrix says Helper ; a Helper-who-is-a-Member can. Same D2 root
Move any member's fundsmoney.md = member-self + admin. Owner-without-admin moving funds is contradicted (C-A)
CRUD own buckets (non-permanent)money.md decision 2; Helper cell depends on D2
Set default buckets (template + permanent)admin.md (isParental)
Manage members (add/remove/role/invite)Admin → Accounts
Configure (catalog/economy/flags/house)personas matrix gives Owner ; admin.md says owner & admin independent → a pure owner should be (C-A)
Billingowner-only; but no billing surface exists on free tier (D3)
View dashboardsdoes a child Member see siblings' wallets/goals on the member-glance widget? Intra-household privacy undefined (X4)
Redeem rewards (request spend)Helper cell depends on whether Helper has a wallet (D2)
Request goalsmember-profile lifecycle; Helper depends on D2
Approve goalsisParental only — Helper explicitly cannot (admin.md), unlike completions
Recommend family budget✓ (edit)✓ (edit)household.md decision 8 (non-admin recommends, admin edits)
View audit logaudit-log open Q: "admins only or owners only?" unresolved

Two structural reads of this table are themselves gaps: Owner is drawn as a strict superset of Admin (personas: "Owner inherits all Admin capabilities") while admin.md treats the marks as independent — so the whole Owner column is contradicted for any owner who is not also an admin.


2. Per-persona gaps & open questions

Member (base)

  • M1 — Watch-only is not in the persona model. HouseholdMember.watchOnly exists in member-profile.md / code.md ("excluded from assignment + active-roster"), but personas.md never places it. Undefined: does a watch-only member have a wallet/goals, can they log in, can they do chores? context.md further scopes it to "Kid (incl. watch-only)" while the flag is generic. Rec: define watch-only as a Member sub-state (tracked, no assignment, no login) and add it to personas.md MemberStatus/overlay discussion.
  • M2 — Shadow child that never graduates. onboarding.md open Q flags the shadow→active promotion for a young child with no phone, but no doc says what acts on a permanent-shadow child's behalf in Today (the parent via profile-switch?). Rec: state that a shadow member is acted-for by an Admin/switched profile; promotion is optional, not required.
  • M3 — No intra-household privacy / opt-out of tracking. Household RLS is "everyone in the house sees everyone." A teen Member cannot hide their wallet/goals/timeline from siblings (child-timeline.md open Q "can siblings view each other's timeline?"). Rec: decide member visibility scoping now — it is an RLS-shaped decision, expensive to retrofit.
  • M4 — Aging-up. No flow for a child crossing 13 / 18 → eligibility for Admin or self-governance (ties to the "continuous kid identity" memory). Rec: capture an age-transition hook even if deferred.

Helper

  • D2 (the Helper root question) — does Helper require a Member profile? personas.md says "every account holder is first a Member," which forces a babysitter to receive a wallet/buckets/ goals they have no use for; yet the matrix shows Helper for do-chores/move-funds (i.e. a standalone helper). The grandparent case wants Member+Helper (tracked, has wallet); the babysitter case wants Helper-only (untracked, no wallet). Rec: allow a non-member / watch-only Helper (untracked adult) distinct from a Member who also holds helper — and fix the matrix to say which column means which.
  • H1 — MemberKind has no slot for a Helper. MemberKind ∈ {parent, coParent, child} (personas.md, self-governed note). A babysitter/grandparent/tutor maps to none of these. Rec: add a kind (e.g. relative / otherAdult) or decouple Helper from kind entirely.
  • H2 — Invite + lifecycle unresolved (roles-permissions.md, personas.md open Qs): full account vs time-limited PIN/link, and whether a helper role expires. Rec: reuse the existing MemberAccess expiry (auth.md) for a time-boxed helper grant; helper joins via a link, not a full co-parent invite.
  • H3 — Helper notifications undefined. notifications.md lists "approval requests" but never says they fan out to Helpers. A babysitter who can approve jobs but gets no approval notification is non-functional. Rec: route completion-approval notifications to parental and helper recipients.
  • H4 — Helper + COPPA. A non-guardian Helper (babysitter) sees a child's photo proofs in the approval queue (chore-photo-submissions.md). childrens-privacy-coppa.md does not address a non-guardian adult viewing minor UGC. Rec: gate helper visibility of child photos, or scope consent to cover it.
  • H5 — Therapist persona has no home (clinical-ot-framework.md open Q: "scoped Helper variant or read-only export link?"). Deferred, but note it as a future Helper sub-variant.

Admin

  • A1 — Zero-admin guard (personas.md, roles-permissions.md open Qs): can the last admin demote/remove themselves? No SDK/RLS invariant. Rec: enforce at-least-one-admin per household in the service AND a schema check (mirrors the other invariants-in-both rule).
  • A2 — Non-parental admin (teen manager) is impossible by definition (Admin = isParental AND admin), yet personas.md/roles-permissions.md keep it open and self-governed mode needs it. Rec: decide whether admin can be granted without isParental; if yes, the COPPA/legal- guardian semantics must move off "admin" onto a separate guardian attribute (see X3).
  • A3 — Co-parent conflict + "delegated scopes." context.md shows "Co-parent (delegated scopes / limited scopes)" but no doc defines those scopes; personas.md/admin.md treat coParent as full isParental admin. Two full admins editing concurrently (one rewrites economy while the other approves) has no conflict/authority model. Rec: reconcile — either coParent is full admin (delete the "delegated scopes" actor language) or define the delegated scope set.

Owner

  • O1 — Owner ⊇ Admin contradiction (headline). personas.md ("Owner inherits all Admin capabilities plus billing"; matrix gives Owner for configure + move-any-funds) vs admin.md ("owner is a distinct attribute… the two marks are independent… An owner is usually also an admin"). A pure owner-without-admin's capabilities are undefined. Rec: make Owner a billing-only overlay that grants nothing in config; require admin separately. Fix the personas matrix Owner column accordingly.
  • O2 — Zero-owner / last-owner deletion (account-profile-management.md open Q "what happens to household data when the last owner deletes their account?"). personas.md lists a zero-admin guard but no zero-owner guard. Rec: block last-owner deletion until ownership is transferred; add a transfer-ownership flow (absent everywhere).
  • O3 — Owner on the free tier. Billing is owner-only, but the free tier has no billing surface (subscription-tiers.md). Who is owner at signup, and what does the Owner overlay do on free? onboarding.md never assigns owner. Rec: creator becomes owner+admin at setup; the Billing surface shows an upgrade CTA on free rather than being empty/hidden.
  • O4 — Paywall routing for a non-owner admin. A non-owner admin who taps a gated feature — does the paywall let them upgrade (they can't see Billing) or nudge an owner? Undefined (subscription-tiers.md). Rec: non-owner admin gets a "ask an owner to upgrade" prompt.

Composition rules

  • C1 — Matrix semantics unpinned (root of D2/O1 and conflicts D, below): each column must declare "isolated overlay grant" vs "effective for a person holding it." Rec: publish the matrix as effective capability for the realistic personas (grandparent, parent) and add a separate "what each overlay adds" line.
  • C2 — Standalone Helper/Owner accounts. The "every account holder is first a Member" rule blocks a clean babysitter-only or billing-only-bookkeeper account. Rec: allow overlay-only access records (untracked) as an explicit exception.

3. Cross-cutting persona gaps

  • X1 — Multi-house roles are per-household but the persona docs read global. household.md models account↔households and member↔households as m:n with roles on the HouseholdMember record (so admin-in-A / member-in-B is supportable), but personas.md/roles-permissions.md never state roles are per-membership. Owner/billing scope across houses is undefined (one bill per account managing two homes, or one subscription per household?). The zero-admin/zero- owner guards (A1/O2) must be per-household. Rec: state "roles + owner are per-household membership" explicitly.
  • X2 — Zero-admin and zero-owner invariants (A1, O2) — enforce in service AND schema, per household; add transfer-ownership and last-admin-protection.
  • X3 — COPPA consent is not bound to a persona. childrens-privacy-coppa.md requires "verifiable parental consent"; terms-consent.md says the admin/parent accepts on a child's behalf — but "verifiable parental consent" is a legal-guardian act, and the model has no guardian attribute distinct from admin (which A2 may decouple from isParental). Rec: bind consent to a isParental legal-guardian, separate from the configurable admin role; define cross-house consent (divorced parents) and child-data erasure-on-behalf (privacy-gdpr.md open Q).
  • X4 — Intra-household visibility vs kid-mode vs dashboards (M3, and conflict E): kid-mode hides other members' profiles, but dashboards/household show every member's wallet/goal chips to any Member. Define what a non-kid-mode child sees of siblings.
  • X5 — Peer/roommate (self-governed) mode is captured in 2026-06-25-self-governed-modes-single-user-peer.md (a governanceMode {familyGuardian, peer, solo} axis). Confirmed deferred; the persona docs' open questions correctly point here. No new gap beyond ensuring the per-household role model (X1) doesn't hard-code one-admin-per-house.
  • X6 — Profile-switching / PIN ownership (conflict F): profile-switching asks "PIN per-profile or household-wide?"; kid-mode says "parent sets a PIN" (implies household-wide, admin-owned). Reconcile to one owner-of-PIN model. Rec: household-wide exit-PIN owned by Admin, optional per-profile lock on top.
  • X7 — Lean-back / TV display = which persona's session? dashboards/tv-desktop-home describe an always-on ambient display, but household-scoped RLS needs a real authenticated session. Undefined: whose credentials power the kiosk, and whether a screen anyone walks past should expose every member's balances. profile-switching ("local display context, not a full auth session") conflicts with RLS needing a session (ties to auth.md deferred wiring). Rec: decide a dedicated display/kiosk identity with a read-only, privacy-reduced household scope.
  • X8 — Approval routing consistency (conflict B): completion approval = "parental OR helper" (admin.md) but dynamics.md flow 1 shows only Parent, and code.md Approval.resolvedBy has only parentManual | autoPolicy (no helperManual). Goal-approval and redemption stay parental — keep that, but the helper-completion decision is not yet wired in the SDK model or the C4 flow.
  • X9 — Audit-log visibility + helper actions (audit-log open Qs): who reads it (admin vs owner), and are helper approvals / failed permission attempts (a child probing Admin) logged? Unresolved.

4. Top 10 decisions to make next (prioritized)

  1. Resolve Owner vs Admin (O1, conflict A): make Owner a billing-only overlay; correct the personas.md matrix Owner column. (Headline contradiction.)
  2. Pin the matrix semantics + define standalone Helper (C1, D2): isolated-grant vs effective; decide whether a babysitter can be a non-member untracked Helper.
  3. Wire the helper-completion-approval decision (X8): add Approval.resolvedBy = helperManual, widen the completion guard to parental OR helper, update dynamics.md flow 1.
  4. Zero-admin + zero-owner invariants, per household, in service AND schema (X2/A1/O2); add transfer-ownership.
  5. Add a MemberKind for non-parent adults / decouple Helper from kind (H1) so grandparent/ babysitter/tutor have a valid identity.
  6. Bind COPPA consent to a legal-guardian attribute distinct from configurable admin (X3); decide if non-parental admin (teen manager) is allowed (A2).
  7. Decide intra-household member visibility (X4/M3) — it is RLS-shaped and expensive later.
  8. Define the helper invite + expiry flow (H2) reusing MemberAccess time-boxing; route helper approval notifications (H3).
  9. Define the lean-back/TV display identity + privacy scope and reconcile PIN ownership (X7/X6).
  10. Specify owner assignment + free-tier billing surface + non-owner-admin paywall (O3/O4) and state multi-house role/owner scoping (X1).

5. Doc-consistency conflicts found

  • A. personas.mdadmin.md — Owner power. personas: "Owner inherits all Admin capabilities" + matrix Owner for configure/move-any-funds. admin.md: owner & admin are "independent… an owner is usually also an admin." → a non-admin owner's capability is contradicted. (O1)
  • B. admin.mddynamics.md / code.md — who approves completions. admin.md: "parental OR helper." dynamics.md flow 1: only Parent. code.md: Approval.resolvedBy = parentManual | autoPolicy (no helper). The C4 flow + enum are stale relative to the persona decision. (X8)
  • C. context.mdpersonas.md / admin.md — co-parent scopes. context.md L1 actor "Co-parent (delegated scopes / limited scopes)"; personas/admin treat coParent as full isParental admin with no scope limiting. (A3)
  • D. personas.md internal — matrix vs prose. Matrix shows Helper for do-chores/move-own- funds (overlay-isolated read); prose says grandparent = Member+Helper (a Member who can do both). Reader-level contradiction from unpinned column semantics. (C1/D2)
  • E. kid-mode-pin-lock.mddashboards.md / household.md — what a child sees. kid-mode: other members' profiles hidden (allowed = Today, limited Catalog, own profile). dashboards/ household: per-member wallet/goal chips + member tiles visible to Members. Define the non-kid-mode child's visibility. (X4)
  • F. profile-switching.mdkid-mode-pin-lock.md — PIN ownership. profile-switching: open Q "per-profile or household-wide?"; kid-mode: "parent sets a PIN" (implies household-wide/admin). Two descriptions of who owns the PIN. (X6)
  • G. context.mdmember-profile.md — watch-only scope. context.md actor "Kid (incl. watch-only)" scopes watch-only to kids; watchOnly is a generic HouseholdMember flag (could be a tracked elder/adult). And watch-only is absent from personas.md entirely. (M1)
  • H. backup-restore-portability.md — "admin/owner can export." Conflates admin vs owner for a data-controller action (GDPR portability); pick one. (minor)