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:

  1. Personal organization data is the canonical source when both personal and family data exist.
  2. Shared finance records do not survive as shared records. Ownership resolves to organization.owner_id.
  3. If multiple subscriptions exist, the personal subscription survives.
  4. Organization.finance_settings moves onto User, using a user-level finance_settings field.
  5. Gamification keeps personal history only and ignores family history.
  6. 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:

  • Membership and Organization are not passive data structures. They are the current runtime control plane.
  • request context flows through Current.membership, session[:organization_id], and Organizations::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_mode and family aggregation logic.
  • some finance records use nullable membership_id to 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.rb
  • app/controllers/application_controller.rb
  • app/models/current.rb
  • app/models/organization.rb
  • app/models/membership.rb
  • app/controllers/organizations/finance_dashboard_controller.rb
  • app/models/dashboard/family_aggregator.rb
  • app/controllers/organizations/subscriptions_controller.rb
  • db/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_id ownership
  • 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:

  • User owns all domain resources directly.
  • Membership is deleted.
  • Organization is deleted.
  • all family-specific behavior is deleted.
  • there is no organization switching.
  • there is no view_mode split between individual and family.
  • 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.rb resolves session[:organization_id], loads @organization, sets Current.membership, and exposes current_view_mode.
  • app/models/current.rb delegates request context through Current.membership and Current.membership.organization.
  • app/controllers/application_controller.rb uses organization context to render the sidebar, locale, exchange rate, and navbar.
  • app/controllers/organization_switch_controller.rb persists personal versus family switching in the session.
  • app/policies/organization/base_policy.rb and descendant policies authorize against membership, not user.
  • app/models/organization.rb owns finance settings, exchange rates, Slack webhook settings, memberships, invitations, requests, and Stripe billing via pay_customer.
  • app/models/membership.rb owns role semantics, gamification profile linkage, challenge participation, and a large share of audit ownership.
  • app/controllers/organizations/finance_dashboard_controller.rb and app/models/dashboard/family_aggregator.rb implement 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 = nil will be reassigned to a single user owner.
  • platform admins remain represented by users.admin.
  • subscriptions will become user-owned.
  • Organization.finance_settings moves onto User, including a new users.finance_settings field.
  • no backward compatibility, dual-write, or legacy read path is required.
  • the database can be reset during implementation.
  • db/seeds.rb must 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:

  1. If a user has both a personal organization and a family organization, the personal organization is the canonical source after cutover.
  2. Shared finance records are not allowed in the final model. Anything that would have been shared should resolve to organization.owner_id.
  3. If a user has more than one active organization subscription, the personal subscription survives.
  4. Organization-level settings move onto User. Specifically, finance_settings should be added to the user model.
  5. Gamification keeps personal history only. Family history is ignored.
  6. 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.rb
  • app/controllers/organizations/base_controller.rb
  • app/controllers/organization_switch_controller.rb
  • app/models/current.rb
  • app/policies/application_policy.rb
  • app/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, not current_user.
  • navbar and sidebar rendering depend on current organization and current membership.

Finance Core

Impacted models include:

  • Account
  • Expense
  • Debt
  • DebtPayment
  • BalanceRegistry
  • Investment
  • Goal

Impacted calculator classes include:

  • Account::BalanceCalculator
  • Expense::SummaryCalculator
  • Investment::PerformanceCalculator
  • Goal::ProgressCalculator
  • Debt::SummaryCalculator
  • Liquidity::Analyzer
  • Dashboard::DataAggregator
  • Dashboard::FamilyAggregator
  • Organization::AnalyticsCalculator

Why this matters:

  • finance queries are scoped by organization.
  • many finance records use optional membership_id to represent shared family ownership.
  • calculators branch on view_mode and visible_to(membership).

Life Management And Tools

Impacted models include:

  • Habit
  • Sport
  • ActivityLog
  • Birthday
  • MarketList
  • MarketListItem
  • Note
  • Countdown
  • ConstructionProject
  • ConstructionExpense
  • TodoBoard
  • TodoCard

Why this matters:

  • these records currently store ownership and visibility through membership_id and organization_id.
  • notes, todo cards, and assignees contain family-aware behavior that is not solved by adding user_id alone.

Gamification And Challenges

Impacted models include:

  • GamificationProfile
  • XpTransaction
  • UserAchievement
  • Challenge
  • ChallengeParticipation

Why this matters:

  • gamification is currently per-membership, not per-user.
  • leaderboards and challenge participation assume an organization boundary.
  • Challenge.scope still supports personal and family.

Access, Roles, And Collaboration

Impacted areas:

  • Membership
  • AccessRequest
  • AccessRequest::InviteToOrganization
  • AccessRequest::UserRequestForOrganization
  • Organizations::MembershipsController
  • Organizations::InvitationsController
  • Organizations::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_customer
  • Organizations::SubscriptionsController
  • SubscriptionHelper
  • 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_id and membership_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:

  1. rewrite the schema to the final User -> Resources model.
  2. remove Membership, Organization, family flows, and shared ownership directly.
  3. move settings and billing to User.
  4. rewrite seeds, tests, and docs to the final model.
  5. 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_id ownership.
  • 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, and access_requests from the schema.
  • remove organization_id and membership_id from surviving tables.
  • add user_id to every surviving user-owned table.
  • add finance_settings to users and 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:migrate produces 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:migrate builds the final schema from scratch.
  • no surviving table depends on organization_id or membership_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.user and current_user the 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_family from finance settings.
  • remove session[:view_mode] and delete individual versus family toggles.
  • delete Dashboard::FamilyAggregator and the family branch in Organizations::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 = family and 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.rb so it creates direct user-owned records only.
  • remove seed logic that depends on personal_organization, memberships, family orgs, or Current.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:seed and confirm the app boots cleanly.
  • run targeted tests and bin/ci once 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, and view_mode leftovers 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:

  1. replace organization and membership ownership with direct user_id
  2. assign any formerly shared finance logic to the personal owner rule
  3. move calculators and queries to user-owned reads
  4. move finance_settings from organization to user

Special risk:

  • shared finance records must disappear entirely from the model and the UI.

Gamification

Recommended order:

  1. make gamification models directly user-owned
  2. keep personal history only
  3. 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:

  1. convert todo_board from organization-owned to user-owned
  2. convert todo_cards.membership_id to user_id
  3. remove assignee_ids collaboration 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:

  1. move Pay ownership directly onto User
  2. keep the personal subscription path only
  3. migrate subscription helpers, checkout, billing portal, and admin tooling
  4. 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.

Use small, domain-bounded PRs instead of one giant branch.

  1. Final-state inventory and schema mapping.
  2. Schema rewrite to direct user_id ownership.
  3. Runtime rewrite for auth, policies, routes, and settings.
  4. Billing rewrite to user ownership.
  5. Family and collaboration removal.
  6. Seeds, tests, and docs rewrite.
  7. 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/ci before 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.

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:

  1. rewrite the schema directly to the final user-owned model
  2. move settings and billing onto User
  3. remove family, shared ownership, and collaboration code entirely
  4. rewrite seeds and tests
  5. 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.