Project Plan: Remove Membership, Organization, and Family
AI Agent Handoff
This document is the implementation plan for removing the current multi-tenant model from Lifehub.
Today the app is built around this chain:
User -> Membership -> Organization -> Resources
The target state is:
User -> Resources
This means the project is not a small association cleanup. It is a repository-wide rewrite of ownership, request context, authorization, billing, seeds, tests, and product behavior.
What This Plan Is For
Another AI agent should read this file as the source of truth for:
- what is being removed
- what the final architecture should be
- which product decisions are already locked
- which parts of the codebase are known hotspots
- the order in which the rewrite should happen
This plan is meant to reduce rediscovery work. An agent should not start by re-debating the architecture unless a new human decision changes the scope.
Final Product Direction
The product direction is already decided:
- there is no family mode anymore
- there is no personal-versus-family switching anymore
- there is no shared financial ownership anymore
- there is no collaborative todo behavior anymore
- memberships disappear completely
- organizations disappear completely
- user-owned settings replace organization-owned settings
- personal data wins whenever personal and family data conflict
Locked Migration Rules
These rules were confirmed and should be treated as requirements, not open questions:
- Personal organization data is the canonical source when both personal and family data exist.
- Shared finance records do not survive as shared records. Ownership resolves to
organization.owner_id. - If multiple subscriptions exist, the personal subscription survives.
Organization.finance_settingsmoves ontoUser, using a user-levelfinance_settingsfield.- Gamification keeps personal history only and ignores family history.
- Todo boards become fully user-owned with zero collaboration semantics.
Key Findings From The Current Codebase
The most important findings from the codebase review are:
MembershipandOrganizationare not passive data structures. They are the current runtime control plane.- request context flows through
Current.membership,session[:organization_id], andOrganizations::BaseController. - authorization depends on membership-based Pundit policies, not direct user ownership.
- billing is organization-owned today.
- finance dashboards and calculators are split by
view_modeand family aggregation logic. - some finance records use nullable
membership_idto represent shared family ownership. - gamification, challenges, leaderboard logic, and todo collaboration all depend on membership semantics.
- the seeds file is deeply tied to personal organizations, family organizations, and
Current.membership.
If an agent removes only the obvious associations and ignores the runtime context, the app will break in subtle ways.
Hotspots To Read First
If an agent needs to re-orient in code before implementation, start with these files:
app/controllers/organizations/base_controller.rbapp/controllers/application_controller.rbapp/models/current.rbapp/models/organization.rbapp/models/membership.rbapp/controllers/organizations/finance_dashboard_controller.rbapp/models/dashboard/family_aggregator.rbapp/controllers/organizations/subscriptions_controller.rbdb/seeds.rb
These files explain most of the current ownership, session, family, and billing behavior.
Execution Constraints
This repository is not in production.
That changes the implementation strategy:
- destructive schema edits are allowed
- migrations can be rewritten directly
- the database can be dropped and recreated
- seeds must be rewritten to the final model
- backward compatibility is not required
- dual-write and temporary compatibility layers are not required
The success condition is not safe coexistence with the old model. The success condition is that a fresh reset produces a coherent app in the new model.
Success Criteria For The Rewrite
The rewrite is only complete when all of the following are true:
- the schema builds cleanly without memberships or organizations
- the app runs using direct
user_idownership - billing and settings are user-owned
- family and shared ownership logic are gone
- seeds work from a clean database reset
- tests and docs reflect the new model
Keep this summary in mind while reading the rest of the plan. The later sections describe the work in implementation order.
Goal
Move Lifehub from this ownership model:
User -> Membership -> Organization -> Resources
to this final model:
User -> Resources
At the end of the project:
Userowns all domain resources directly.Membershipis deleted.Organizationis deleted.- all family-specific behavior is deleted.
- there is no organization switching.
- there is no
view_modesplit betweenindividualandfamily. - billing, settings, permissions, and notifications are user-scoped.
This repository is not in production. The plan therefore assumes we can make direct destructive changes, rewrite migrations, drop the database, recreate it, and reseed from scratch.
This plan is still phased, but for execution discipline only. It is not a backward-compatibility rollout plan.
What The Codebase Looks Like Today
The current app is not only using organization_id and membership_id as foreign keys. It is using them as the main execution context for almost every authenticated request.
Primary control points today:
app/controllers/organizations/base_controller.rbresolvessession[:organization_id], loads@organization, setsCurrent.membership, and exposescurrent_view_mode.app/models/current.rbdelegates request context throughCurrent.membershipandCurrent.membership.organization.app/controllers/application_controller.rbuses organization context to render the sidebar, locale, exchange rate, and navbar.app/controllers/organization_switch_controller.rbpersists personal versus family switching in the session.app/policies/organization/base_policy.rband descendant policies authorize againstmembership, notuser.app/models/organization.rbowns finance settings, exchange rates, Slack webhook settings, memberships, invitations, requests, and Stripe billing viapay_customer.app/models/membership.rbowns role semantics, gamification profile linkage, challenge participation, and a large share of audit ownership.app/controllers/organizations/finance_dashboard_controller.rbandapp/models/dashboard/family_aggregator.rbimplement the current family data model.
That means this project is not a column rename. It is a full request-context rewrite plus a data ownership rewrite.
Planning Assumptions
This plan assumes the product decision is:
- one user owns one logical dataset.
- family collaboration is removed, not redesigned.
- invitations, membership requests, and member management are removed, not replaced.
- shared records that currently use
membership_id = nilwill be reassigned to a single user owner. - platform admins remain represented by
users.admin. - subscriptions will become user-owned.
Organization.finance_settingsmoves ontoUser, including a newusers.finance_settingsfield.- no backward compatibility, dual-write, or legacy read path is required.
- the database can be reset during implementation.
db/seeds.rbmust be rewritten to the new direct-user model.
If any of those assumptions change, the rollout plan must change before implementation starts.
Decisions Locked For Implementation
These decisions are now fixed and should drive the implementation:
- If a user has both a personal organization and a family organization, the personal organization is the canonical source after cutover.
- Shared finance records are not allowed in the final model. Anything that would have been shared should resolve to
organization.owner_id. - If a user has more than one active organization subscription, the personal subscription survives.
- Organization-level settings move onto
User. Specifically,finance_settingsshould be added to the user model. - Gamification keeps personal history only. Family history is ignored.
- The todo board is not collaborative in the final model. It becomes fully user-owned.
These are no longer open questions. They are implementation rules.
Scope By Domain
Core Context And Auth
Impacted areas:
app/controllers/application_controller.rbapp/controllers/organizations/base_controller.rbapp/controllers/organization_switch_controller.rbapp/models/current.rbapp/policies/application_policy.rbapp/policies/organization/base_policy.rb- nearly every controller under
app/controllers/organizations/
Why this matters:
- every authenticated request depends on organization resolution.
- Pundit currently receives
Current.membership, notcurrent_user. - navbar and sidebar rendering depend on current organization and current membership.
Finance Core
Impacted models include:
AccountExpenseDebtDebtPaymentBalanceRegistryInvestmentGoal
Impacted calculator classes include:
Account::BalanceCalculatorExpense::SummaryCalculatorInvestment::PerformanceCalculatorGoal::ProgressCalculatorDebt::SummaryCalculatorLiquidity::AnalyzerDashboard::DataAggregatorDashboard::FamilyAggregatorOrganization::AnalyticsCalculator
Why this matters:
- finance queries are scoped by organization.
- many finance records use optional
membership_idto represent shared family ownership. - calculators branch on
view_modeandvisible_to(membership).
Life Management And Tools
Impacted models include:
HabitSportActivityLogBirthdayMarketListMarketListItemNoteCountdownConstructionProjectConstructionExpenseTodoBoardTodoCard
Why this matters:
- these records currently store ownership and visibility through
membership_idandorganization_id. - notes, todo cards, and assignees contain family-aware behavior that is not solved by adding
user_idalone.
Gamification And Challenges
Impacted models include:
GamificationProfileXpTransactionUserAchievementChallengeChallengeParticipation
Why this matters:
- gamification is currently per-membership, not per-user.
- leaderboards and challenge participation assume an organization boundary.
Challenge.scopestill supportspersonalandfamily.
Access, Roles, And Collaboration
Impacted areas:
MembershipAccessRequestAccessRequest::InviteToOrganizationAccessRequest::UserRequestForOrganizationOrganizations::MembershipsControllerOrganizations::InvitationsControllerOrganizations::MembershipRequestsController- related notifiers
Why this matters:
- all collaboration flows disappear in the target product.
- these features cannot be left half-migrated because their purpose vanishes once family is removed.
Billing, Pricing, And Admin
Impacted areas:
Organization#pay_customerOrganizations::SubscriptionsControllerSubscriptionHelper- admin organization screens
- affiliate subscription routing
Why this matters:
- billing is organization-owned today.
- a user can effectively have multiple subscriptions today through multiple organizations.
- this is the highest-risk area for revenue-impacting regressions.
Tests, Fixtures, Docs, And Copy
Impacted areas:
db/seeds.rb- fixtures with
organization_idandmembership_id - controller, model, integration, and system tests
- product copy that mentions personal or family organizations
- docs that describe the multi-tenant model
Why this matters:
- the migration is not done until seeds, tests, and documentation match the new model.
Delivery Strategy
Because the app is not in production, the delivery strategy should be a direct final-state rewrite.
Do not build temporary compatibility layers, dual-write paths, or legacy read paths.
Instead:
- rewrite the schema to the final
User -> Resourcesmodel. - remove
Membership,Organization, family flows, and shared ownership directly. - move settings and billing to
User. - rewrite seeds, tests, and docs to the final model.
- reset the database with
bin/rails db:drop db:create db:migrate db:seed.
Phase Plan
Phase 0: Inventory And Final-State Mapping
Deliverables:
- confirm the final target architecture.
- map every surviving model to direct
user_idownership. - identify every file that must be deleted because family, memberships, or organizations disappear.
Tasks:
- produce a table of every model with
membership_id,organization_id, or organization-scoped behavior. - classify each model as either: survives with
user_id, survives with simplified behavior, or is deleted entirely. - list the exact family-only flows to remove.
- list the exact migration and seed files that must be rewritten.
Exit criteria:
- the final-state mapping is complete.
- there are no open architecture questions left.
Phase 1: Rewrite Schema To Final State
Objective:
- change the database structure directly to the final model, without compatibility columns or transition code.
Tasks:
- remove
memberships,organizations, andaccess_requestsfrom the schema. - remove
organization_idandmembership_idfrom surviving tables. - add
user_idto every surviving user-owned table. - add
finance_settingstousersand move any other surviving org settings there. - move billing ownership to
users. - simplify todo board storage so it is fully user-owned and non-collaborative.
- rewrite or replace migrations so
db:migrateproduces the final schema from an empty database.
Representative model groups:
- finance: accounts, expenses, debts, debt payments, balance registries, investments, goals
- life tools: habits, sports, activity logs, birthdays, market lists, notes, countdowns, construction projects, construction expenses
- gamification: gamification profiles, xp transactions, user achievements, challenge participations, challenges
- boards: todo boards and todo cards
Exit criteria:
db:migratebuilds the final schema from scratch.- no surviving table depends on
organization_idormembership_id.
Phase 2: Rewrite Runtime Ownership, Auth, And Billing
Objective:
- make the application run entirely on direct user ownership.
Tasks:
- remove
Current.membership,Current.organizations,current_organization, and session-based organization switching. - make
Current.userandcurrent_userthe only ownership context. - rewrite policies to authorize against users, not memberships.
- rewrite controllers, helpers, jobs, notifiers, and calculators to query by
user_id. - move subscription helpers, checkout flow, and billing portal flow to user-owned billing.
- move exchange-rate, locale, and finance settings reads to
users.finance_settings. - rename or delete organization-scoped routes and controller namespaces where appropriate.
Exit criteria:
- the app boots and runs without any runtime dependency on memberships or organizations.
- billing, locale, settings, and authorization all work from user-owned data.
Phase 3: Remove Family, Shared Ownership, And Collaboration Behavior
Objective:
- remove every feature path that only exists because of family, shared ownership, or multi-member behavior.
Tasks:
- remove
create_familyfrom finance settings. - remove
session[:view_mode]and delete individual versus family toggles. - delete
Dashboard::FamilyAggregatorand the family branch inOrganizations::FinanceDashboardController. - remove family-specific UI copy from pricing, settings, navbar, and dashboard surfaces.
- remove member management, invitations, membership requests, and related notifications.
- remove
Challenge.scope = familyand convert challenges to user-owned personal challenges only. - remove todo assignee collaboration entirely or collapse it to a single user owner.
- rewrite notes, goals, debts, investments, expenses, and related UI to stop distinguishing between personal and shared ownership.
- keep personal gamification only and ignore family history.
Exit criteria:
- no page in the app offers family creation, member management, invites, membership requests, family dashboards, or family view toggles.
- no active business flow depends on shared or collaborative ownership.
Phase 4: Rewrite Seeds, Tests, Docs, And Reset The App
Objective:
- make the repository runnable and testable from a fresh database in the final model.
Tasks:
- rewrite
db/seeds.rbso it creates direct user-owned records only. - remove seed logic that depends on
personal_organization, memberships, family orgs, orCurrent.membership. - update fixtures and automated tests to the new schema.
- update documentation and product copy to remove membership, organization, and family terminology.
- run
bin/rails db:drop db:create db:migrate db:seedand confirm the app boots cleanly. - run targeted tests and
bin/cionce the new seeds and schema are stable.
Exit criteria:
- a fresh database reset produces a working app.
- seeds, tests, and docs all match the final model.
Phase 5: Final Cleanup
Objective:
- remove dead files, dead constants, and dead naming that survived the direct rewrite.
Tasks:
- delete leftover organization or membership files that are no longer referenced.
- remove dead routes, helpers, policies, jobs, and notifiers.
- clean up schema annotations and comments that still mention the old model.
- search the repository for
organization,membership,family_organization,personal_organization, andview_modeleftovers and remove the invalid ones.
Exit criteria:
- the repository no longer contains meaningful runtime references to the old tenancy model.
- the codebase reads as a direct user-owned system rather than a migrated tenant system.
Per-Domain Migration Notes
Finance
Recommended order:
- replace organization and membership ownership with direct
user_id - assign any formerly shared finance logic to the personal owner rule
- move calculators and queries to user-owned reads
- move
finance_settingsfrom organization to user
Special risk:
- shared finance records must disappear entirely from the model and the UI.
Gamification
Recommended order:
- make gamification models directly user-owned
- keep personal history only
- remove family challenge semantics and org-scoped leaderboards
Special risk:
- forgetting to ignore family history will leak the wrong XP and achievement totals into the final model.
Todo Board
Recommended order:
- convert
todo_boardfrom organization-owned to user-owned - convert
todo_cards.membership_idtouser_id - remove
assignee_idscollaboration behavior entirely unless it is simplified to self-only semantics
Special risk:
- this feature still encodes collaboration in JSON fields, so the cleanup must cover both database columns and serialized data structures.
Billing
Recommended order:
- move Pay ownership directly onto
User - keep the personal subscription path only
- migrate subscription helpers, checkout, billing portal, and admin tooling
- delete org-owned subscription assumptions
Special risk:
- this is still the most fragile workstream because billing logic touches revenue and affiliate behavior, even in a non-production rewrite.
Recommended Pull Request Sequence
Use small, domain-bounded PRs instead of one giant branch.
- Final-state inventory and schema mapping.
- Schema rewrite to direct
user_idownership. - Runtime rewrite for auth, policies, routes, and settings.
- Billing rewrite to user ownership.
- Family and collaboration removal.
- Seeds, tests, and docs rewrite.
- Final cleanup and repository-wide reference sweep.
Each PR should leave the repository runnable from a fresh database reset.
Validation And Rollout Gates
Every phase should define explicit checks before rollout.
Minimum validation set:
bin/rails db:drop db:create db:migrate db:seed- regression tests for billing, authentication, dashboard, accounts, expenses, goals, habits, notes, todo board, challenges, and notifications.
- policy tests proving that direct user ownership is enforced.
- repository search checks for stale references to memberships, organizations, family dashboards, and view-mode logic.
- local CI via
bin/cibefore merging high-risk phases.
Recommended rollout guardrails:
- keep the branch runnable after each major phase.
- prefer direct deletion over temporary compatibility code.
- do not consider the work done until a clean database reset succeeds.
- validate billing paths separately after moving them to
User.
Rollback Strategy
Because this app is not in production, rollback does not require a compatibility strategy.
That means:
- revert the branch in git if needed.
- reset the database and reseed if needed.
- keep seeds and migrations correct so the repository can always be rebuilt from zero.
The important safety mechanism here is repository rebuildability, not runtime backward compatibility.
Recommended Staffing And Timeline
This should be treated as a multi-workstream refactor, not a background cleanup task.
Suggested staffing:
- one engineer focused on schema and data migration
- one engineer focused on billing, auth, and request context
- one engineer focused on product surfaces, family removal, and test migration
Suggested timeline:
- Phase 0: 1 to 2 days
- Phase 1: 4 to 7 days
- Phase 2: 4 to 7 days
- Phase 3: 2 to 4 days
- Phase 4: 2 to 4 days
- Phase 5: 1 to 2 days
Total: roughly 2.5 to 5 weeks, depending mainly on billing, tests, and how much dead family logic is embedded in the UI and calculators.
Recommendation
If the requirement is truly "remove memberships and organizations everywhere without breaking anything," the safest path is:
- rewrite the schema directly to the final user-owned model
- move settings and billing onto
User - remove family, shared ownership, and collaboration code entirely
- rewrite seeds and tests
- validate the system from a clean database reset
Because this is not in production, a direct rewrite is appropriate. The discipline you still need is not compatibility. It is finishing the schema, runtime, seeds, and tests together so a fresh reset gives you a coherent app.