Skip to main content

SP‑D — People & Access Admin Page — Design Spec

Date: 2026-07-24 Status: Draft for owner review Initiative: Onboarding + invite + account-management + multi-household (sub-project D of 4 — the final one). Vision: docs/decisions/2026-07-22-onboarding-invite-account-management-vision.md (capability #5). SP‑A (multi-household foundation), SP‑B (code-first onboarding + auto-detect invite redemption), and SP‑C (companion durable dewdrops) are all SHIPPED to prod. Branch: feat/mvp1-personas-authz.

Goal

End the scatter: member/people management currently lives in two overlapping places (the Household tab owns the roster + editor + add-member/add-adult + child-approvals; the /governance page owns a second roster with roles + consents) plus a pile of invite/code sheets. SP‑D consolidates all people-and-access management into one admin page, turns the Household tab into a friendly read-only family view, and keeps fast household-switching universal on the More tab.

Locked decisions (owner, 2026-07-23/24)

  1. One "People & Access" admin page. /governance expands into the single admin home for people (roster + roles/owner + consents + invites + member↔account linking + child-approvals + add-adult). Admin-gated (AdminGuard/Capability.manageRoles).
  2. Household tab → read-only family view. The main-shell Household tab keeps the family roster (names/avatars, NOT editable) + family goals — a warm "who's in our family" home everyone (incl. kids) sees. ALL admin actions leave it.
  3. Switching stays universal on More; the page gets an admin Households section. The fast quick-switch (tappable household name → switcher sheet with Join/Create) stays on the More tab for any adult. The People & Access page adds a Households tab for the management view (see every household you're in; Join/Create from here too; future leave/delete-and-merge). Switching is NOT moved off More (it must stay reachable by non-admin multi-household members).

Architecture

1. The page + navigation

  • The existing GovernanceRoute (app/lib/inside/routes/authenticated/governance/page.dart) is expanded into the People & Access page, retaining AdminGuard (Capability.manageRoles OR billing owner). A DsSegmented<PeopleAccessTab> selector (mirroring ManagePage) with two tabs: Members and Households.
  • Re-pointed entries: the Admin hub's "Roles & owners" row (AdminHubPage.rolesOwnersEntry) → "People & Access"; the Household-tab TopLevelHeader shield button (MembersPage.governanceButton) → this page. The old two-DsSection governance layout (Governance + Consent) is subsumed into the Members tab.

2. Members tab

The single home for member admin, absorbing what is scattered today:

  • Roster: every HouseholdMember as a row — name/avatar + role badges (admin/helper/member) + owner badge + link status (linked/unlinked/invited) + (children) consent DsStatusChip (+ "Needs re-consent" from checkConsentCurrency).
  • Per-member management (tap a row → sheet): one surface consolidating today's separate RoleOwnerEditorSheet + invite/code sheets:
    • Role toggles (member base-locked / admin / helper — admin+helper require a linked account) → setRole.
    • Owner toggle → grantOwner/revokeOwner; transfer ownership → transferOwnership.
    • Consent (children): capture (owner-only, captureConsent) / revoke (revokeConsent) + stale indicator.
    • Link actions: unlinked adult → Invite to account (account_invite_sheet flow, inviteMember); unlinked child → Generate sign-up code (child_signup_code_sheet flow, issueChildAttachCode).
    • Remove member (danger, removeMember).
  • Top-of-tab actions: Add (add-adult via create_adult_sheet/adminCreateAdult; add-child via member_editor_sheet add-mode) + invite co-parent (inviteCoParentco_parent_code_sheet).
  • Pending queue (highlighted section with counts): child-approvals needing VPC (pendingConsent children → activate/decline/convert-to-adult) + pending co-parent invites (_InvitedMemberTile resend/revoke via resendInvite/revokeInvite).

3. Households tab

  • Lists every household the account belongs to (listMyHouseholds) — name/emoji + active marker.
  • Join a household + Create another — the SAME flows as the More switcher (showJoinWithCodeSheet + SetupRoute(isCreatingAdditional: true)), shared code, not a re-implementation.
  • The fast quick-switch stays on More (HouseholdHeaderBlockshowHouseholdSwitcherSheet). This tab is the richer/management view.
  • Deferred slot: leave-household + delete-and-merge (the SP‑C north-star) land here later; SP‑D leaves a clean seam, builds neither.

4. Household tab (main shell) → read-only family view

  • HouseholdPage keeps: the family roster (names/avatars, non-editable — tapping a member still opens the read-only MemberProfileRoute, but no admin affordances) + the Family Goals section.
  • Removed from the Household tab: the three TopLevelHeader admin buttons (governance shield, add-member +, add-adult), the ChildApprovalsSection, the MemberEditorSheet add/edit affordances, and the invite/code launch points — all move to the People & Access page.
  • The tailored (child) view (wallet + personal goals + family goals) is unchanged.

5. Reuse + fold-in

  • The invite/code sheets (account_invite_sheet, child_signup_code_sheet, household_child_join_code_sheet incl. the SP‑B member-binding + current-code note, co_parent_code_sheet) are reused verbatim as flows launched from the per-member surface / add actions. The shared invite_code_card is unchanged.
  • join_with_code_sheet + the switcher stay on More; the Households tab reuses them.
  • Fold in the deferred SP‑B pieces: already_member → "offer to switch" in the join flow (peek already returns the household; surface a "switch to it" action instead of a plain error); and any generate-side polish SP‑B kept minimal.

6. Bloc consolidation (targeted cleanup)

  • Today RoleOwnerBloc (governance) and MembersBloc (household) both stream the member roster (watchMembers) — a real duplication. SP‑D consolidates the Members tab onto a single bloc that owns roster + roles + owner + consents + invites + approvals (reusing the logic from both — _runGuarded actor resolution, checkConsentCurrency, the invite/remove verbs). The read-only Household tab gets a thin roster read (no admin events). The Households tab reuses listMyHouseholds (as MoreBloc does).
  • This is in-scope cleanup because SP‑D touches both blocs anyway; do not refactor unrelated code.

7. Error handling

  • Typed errors only (on <SpecificException>; never bare catch, never Error) — reuse the existing _runGuarded pattern that maps DomainRuleException → a themed failure dialog. AuthorizationFailure/DomainRuleException from role/consent/invite verbs surface as themed dialogs; no raw errors.

8. Testing

  • Bloc/unit: the consolidated Members bloc (roster stream; each role/owner/consent/invite/remove verb; guarded-actor failure → failure state; pending-approval activate/decline). The Households view (list + join/create routing). The read-only Household-tab path (no admin affordances present).
  • Flow (one feature-set flowTest, multiple stories per the flow-test rule): People-&-Access epic — stories: manage-a-member (open member → toggle role/owner → consent capture), add-adult, switch-and-join (Households tab list + join). Plus a Household-tab read-only-view story (admin buttons absent for a plain member; present-but-moved for admin).
  • Reuse the flow-test harness + MocksContainer; baselines must not drop (SDK ~1214, app ~838 at branch head).

Global constraints

  • One data path: Bloc → Repository → Client facade → Service → Adapter; presentation never imports drift/supabase.
  • All admin mutations remain server-authoritative (roles/consents/invites already are — SP‑D is a UI/IA consolidation, NOT a new-authorization surface; no RLS/capability changes). No new migration expected (this is a client-side consolidation; flag if any server gap surfaces).
  • FVM only; no brand strings in package/class/file names; all copy via Strings; typed errors only.
  • Flow tests scoped to EPIC + FEATURE SET, ONE flowTest carrying multiple stories.

Scope boundary

In SP‑D: the People & Access page (two tabs) consolidating roster + roles/owner + consents + invites + member↔account linking + child-approvals + add-adult; the Households management tab (list + join/create, reusing existing flows); re-pointing the Admin-hub + Household-tab entries; the Household tab → read-only family view; the bloc consolidation; folding the deferred SP‑B already_member-offer-to-switch + generate-side polish; tests.

NOT in SP‑D: the Admin-hub "Account" placeholders (Billing / Notifications / Data & Privacy — separate, mostly-unbuilt; not user-management); leave-household / delete-and-merge / GDPR erasure (SP‑C north-star — a clean seam is left in Households, nothing built); any new authorization model or RLS change; the M2 companion growth-stage durability ticket (unrelated).

Open questions for owner review

  1. Members tab: sheet vs inline panel for per-member management. Recommend keeping it a sheet (reuses today's RoleOwnerEditorSheet/editor pattern, least churn) rather than an inline expanding panel. Confirm?
  2. Pending queue placement — a highlighted section at the top of the Members tab (recommended, keeps it seen), or its own third tab? Recommend top-of-Members with a count.