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)
- One "People & Access" admin page.
/governanceexpands into the single admin home for people (roster + roles/owner + consents + invites + member↔account linking + child-approvals + add-adult). Admin-gated (AdminGuard/Capability.manageRoles). - 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.
- 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, retainingAdminGuard(Capability.manageRolesOR billing owner). ADsSegmented<PeopleAccessTab>selector (mirroringManagePage) with two tabs: Members and Households. - Re-pointed entries: the Admin hub's "Roles & owners" row (
AdminHubPage.rolesOwnersEntry) → "People & Access"; the Household-tabTopLevelHeadershield button (MembersPage.governanceButton) → this page. The old two-DsSectiongovernance 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
HouseholdMemberas a row — name/avatar + role badges (admin/helper/member) + owner badge + link status (linked/unlinked/invited) + (children) consentDsStatusChip(+ "Needs re-consent" fromcheckConsentCurrency). - 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_sheetflow,inviteMember); unlinked child → Generate sign-up code (child_signup_code_sheetflow,issueChildAttachCode). - Remove member (danger,
removeMember).
- Role toggles (member base-locked / admin / helper — admin+helper require a linked account) →
- Top-of-tab actions: Add (add-adult via
create_adult_sheet/adminCreateAdult; add-child viamember_editor_sheetadd-mode) + invite co-parent (inviteCoParent→co_parent_code_sheet). - Pending queue (highlighted section with counts): child-approvals needing VPC (
pendingConsentchildren → activate/decline/convert-to-adult) + pending co-parent invites (_InvitedMemberTileresend/revoke viaresendInvite/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 (
HouseholdHeaderBlock→showHouseholdSwitcherSheet). 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
HouseholdPagekeeps: the family roster (names/avatars, non-editable — tapping a member still opens the read-onlyMemberProfileRoute, but no admin affordances) + the Family Goals section.- Removed from the Household tab: the three
TopLevelHeaderadmin buttons (governance shield, add-member+, add-adult), theChildApprovalsSection, theMemberEditorSheetadd/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_sheetincl. 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 sharedinvite_code_cardis 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) andMembersBloc(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 —_runGuardedactor resolution,checkConsentCurrency, the invite/remove verbs). The read-only Household tab gets a thin roster read (no admin events). The Households tab reuseslistMyHouseholds(asMoreBlocdoes). - 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 barecatch, neverError) — reuse the existing_runGuardedpattern that mapsDomainRuleException→ a themed failure dialog.AuthorizationFailure/DomainRuleExceptionfrom 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
- 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? - 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.