Personas & access model
Status: ✅ Authorization model shipped (Authorizer + Capability in client_sdk) · 🔨 Admin
UI for role assignment partial. The capability sets below are transcribed from
services/authorizer.dart — they are the shipped model, not a proposal.
The four personas
Every account holder is first a Member — the base layer. The other three personas are
composable overlays: a member can hold any combination of them. For example a grandparent
(MemberKind.otherAdult — a tracked non-guardian adult) is typically Member + Helper — a
tracked person with their own profile/wallet/goals who also approves completed jobs (babysitting).
A parent is Member + Admin (+ Owner if they handle billing). So "Helper", "Admin", and
"Owner" describe what a member can additionally do (role / flag overlays), not a separate kind of
user.
Member (base)
The universal persona: kids, parents, other adults, roommates, anyone in the household.
MemberKind ∈ {parent, coParent, otherAdult, child} and MemberRole.member by default.
Members own a personal wallet, buckets, goals, and budget (see Money & Envelopes).
"Roommate" is a Member in the deferred peer/self-governed house mode (see Open questions).
MemberKind.otherAdult is a tracked adult who is not a legal guardian — a grandparent,
babysitter, or tutor. It carries an adult identity (own profile/wallet/goals) but isParental is
false, so the Authorizer's intrinsic parental-kind gate strips the consent / supervise /
approve-spend / approve-goal / erase verbs from it. An otherAdult can hold the Helper role
overlay (the typical babysitter/grandparent shape) but, by the MVP-1 rules that admin ⇒ isParental
(code-backed: enforced at HouseholdService.setRole/setMemberRole via _requireRoleKind,
which throws DomainRuleException on an admin grant to a non-parental kind) and owner ⇒ isParental,
can never be an Admin or Owner — and, because a consent target must be
a MemberKind.child, can never be a consent target. (Closes persona-lens H1.)
Watch-only is a boolean flag on a Member (HouseholdMember.watchOnly), not a MemberStatus
value. A watch-only member is tracked (in the roster, has a profile) but is excluded from chore
assignment, the active roster, and the earning surfaces, and has no login. It is a Member
sub-state for someone represented in the household without participating in the token economy (e.g. a
young child or an elder you are only tracking). It is a generic flag — not kid-only.
Helper
A trusted non-admin adult: babysitter, grandparent, tutor.
MemberRole.helper — a role overlay decoupled from MemberKind (it layers on top of any kind,
typically otherAdult). Narrowest management set by design: the helper overlay grants exactly
approveCompletion + viewHouseholdAll (verified in authorizer.dart _helperCaps). A Helper
can approve or deny completed chore submissions and see the household roster — no Admin
access, no fund moves, no config, and never spend-request or goal approvals.
approveCompletion is deliberately not a parental-only verb, so a non-parental otherAdult
Helper keeps it. This is the explicit decision from the Admin architecture (see Admin).
Admin
A legal guardian or designated household manager.
MemberKind.isParental (i.e. parent or coParent) AND MemberRole.admin (the
admin ⇒ isParental rule is kept for MVP-1; decoupling non-parental admins is deferred — see
Open questions). The admin overlay (_adminCaps) controls everything in the Admin area: catalog
CRUD, member management, roles, economy config, feature flags, default buckets, house settings, plus
the completion / spend-request / goal approvals and createFamilyGoal / completeGoal. Note
that capturing / revoking COPPA consent is NOT an admin verb — it is owner-only (see Owner,
and the consents_insert RLS).
Owner
The billing / ownership overlay: the owner: bool flag on a member — a distinct overlay, NOT
an Admin superset. The owner flag grants exactly manageBilling, deleteHousehold,
manageOwners, captureConsent, revokeConsent (verified in authorizer.dart _ownerCaps) and
nothing else — a pure owner (owner without admin) cannot configure the household. owner ⇒ isParental is an invariant enforced upstream (schema trigger + HouseholdService).
Multiple owners allowed (co-parents can both be owners). The realistic "account owner" persona is
Member + Admin + Owner (the household creator is assigned owner + admin at setup); the Owner
column below shows the overlay in isolation, not that composite.
Capability matrix (effective — transcribed from authorizer.dart)
Read each column as the effective capability of a person holding that overlay, except Owner,
which is shown as the billing / ownership overlay in isolation (it is not an Admin superset —
the headline O1 fix). ✓ = holds the verb, ✗ = does not. The realistic account owner is
Member + Admin + Owner.
| Capability (verb) | Member | Helper | Admin | Owner |
|---|---|---|---|---|
Do / claim chores, view own wallet (_baseSelfCaps) | ✓ | ✓ | ✓ | ✗ |
Own wallet moves, goals & redemptions (_selfEconomyCaps) † | ✓ | † | ✓ | ✗ |
View whole household (viewHouseholdAll) | ✗ | ✓ | ✓ | ✗ |
Approve completed jobs (approveCompletion) | ✗ | ✓ | ✓ | ✗ |
Approve spend requests (approveSpendRequest) | ✗ | ✗ | ✓ | ✗ |
Approve goal requests (approveGoalRequest) | ✗ | ✗ | ✓ | ✗ |
Move any member's funds (manageBudget) | ✗ | ✗ | ✓ | ✗ |
| Configure household (catalog/economy/flags/rooms/members/roles) | ✗ | ✗ | ✓ | ✗ |
Capture / revoke COPPA consent (captureConsent/revokeConsent) | ✗ | ✗ | ✗ | ✓ |
Billing (manageBilling) | ✗ | ✗ | ✗ | ✓ |
Delete household / manage owners (deleteHousehold/manageOwners) | ✗ | ✗ | ✗ | ✓ |
† Self-economy is adult/consent-gated. _selfEconomyCaps (createOwnGoal, editOwnGoal,
archiveGoal, requestGoal, redeemReward, moveOwnFunds, manageOwnEnvelopes) is held by
every adult kind (parent/coParent/otherAdult) unconditionally and by a child only when
ConsentState.granted (the COPPA gate). As shipped the gate is isAdult || consent == granted
(CB-14), so a default otherAdult (non-parental but still an adult, consentState = none) holds
self-economy unconditionally — the consent-conditional † applies only to a child. The
Helper † cell therefore resolves to ✓ for the typical otherAdult Helper and is consent-gated
only when the underlying member is a child. A non-active member (shadow/invited) and a lapsed
time-boxed access window both resolve to no capabilities at all.
What each overlay adds
The Authorizer unions these grants (owner flag + each role + the base self-row), then applies an
intrinsic parental-kind gate that strips {captureConsent, revokeConsent, eraseChildData, superviseAction, approveSpendRequest, approveGoalRequest} for any non-parental kind — even when a
role would otherwise grant them. approveCompletion is not in that stripped set.
- Member (base, every active member):
submitChore,viewOwnWallet,viewSharedFamilyGoals,editOwnPreferences,adjustOwnRunEstimate;editOwnProfilefor adult kinds (parent/coParent/otherAdult); the adult/consent-gated_selfEconomyCaps(see †). - Helper overlay (
MemberRole.helper): addsapproveCompletion+viewHouseholdAll. Nothing else, ever. - Admin overlay (
MemberRole.admin, MVP-1 requiresisParental): addsremoveMember,manageRoles,manageHousehold,manageCatalog,manageRoutines,manageBudget,inviteMember,manageMemberProfile,manageNotificationPrefs,printList,eraseChildData,approveCompletion,approveSpendRequest,approveGoalRequest,superviseAction,viewHouseholdAll,createFamilyGoal,completeGoal. - Owner overlay (
ownerflag): addsmanageBilling,deleteHousehold,manageOwners,captureConsent,revokeConsent. Not an Admin superset.
Contested-stance pointer. This matrix reflects the shipped (conservative as-shipped) code. Four HIGH-STAKES ratifications — Owner-vs-Admin as-shipped, intra-household RLS visibility, guardian-vs-admin decoupling, and earn auto-split vs an "Unallocated" bucket — are tracked in the consolidated backlog (
docs/superpowers/plans/2026-06-28-p1p2-consolidated-backlog.md, HS-1..HS-4) and remain awaiting user sign-off. The docs describe the current code, not a final ruling on those four.
Active-member lens ≠ actor
The Profile-page active-member switcher lets a capable viewer proxy into another
household member's view (companion, avatar, catalogs, My Cosmos). This is a display
lens only (SelectedMemberRepository): the authenticated member
(CurrentMemberRepository.current) remains the sole actor for every gated call and
for RLS — proxying never elevates authorization. While proxied (active member ≠
authenticated self), member-owned surfaces are read-only (the SDK's self-only
companion mutation enforces it). Full "act-as" IAM is a future phase, out of scope. See
Authorization ("Viewing as" is not authorization) for the
invariant-lock detail.
MemberStatus overlay
MemberStatus ∈ {shadow, invited, active} is a lifecycle state, not a permission tier.
A shadow member is a placeholder created by the admin (no login credentials yet).
invited = credentials issued, acceptance pending. active = fully onboarded.
Roles are per-household membership
Roles (MemberRole) and the owner flag are per-HouseholdMember membership, not global to an
account. The same person can be admin in household A and a plain member in household B. One
subscription is per account (an account may manage N homes); ownership/billing scope and the
zero-admin / zero-owner guards are evaluated per household. See
Roles & permissions and Household (multi-household model).
Open questions
- Peer/self-governed mode. Roommate households with no single admin are a deferred research direction. The multi-house member model (member ↔ many households) is forward-compatible, but the permission rules for a "peer" house need design.
- Non-parental admin (teen manager). Whether
admincan be granted without theisParentalgate is deferred — it is bound up with self-governed mode and COPPA guardian semantics (consolidated backlog HS-3 / DF-6). MVP-1 keepsadmin ⇒ isParental. - Helper invite flow. How a standalone (non-member, time-boxed) babysitter Helper joins — full
account creation vs a time-limited PIN/link reusing the
MemberAccessexpiry window — is deferred (backlog DF-1). Does a helper ever graduate to member?
Resolved: the zero-admin / zero-owner self-demotion guards are now enforced (per household, in the service AND a schema trigger) alongside a transfer-ownership flow — no longer open questions.
Relationship
- Admin: the Admin area is the entire config surface for the Admin/Owner personas.
- Money & Envelopes: Member-scoped wallets, buckets, and goals; move-funds permissions.
- Roles & permissions: the SDK model, Admin UI for assignment, invite flows.