Skip to content

feat(dx): adopter-owned app zones for easier template upgrades #369

@ZappoMan

Description

@ZappoMan

Business need

Every Beaker Stack fork diverges immediately — that is expected and healthy. Adopters customize branding, marketing, pricing, policies, dashboard UI, and eventually their own database schema. They rarely modify core template features (admin panel, billing package, email packages, waitlist, observability).

The problem today: adopter-owned customization is scattered across the repo (packages/shared, apps/web/src/billing/, apps/web/public/landing/, assets/, mobile mirrors, etc.). When a fork merges a new CalVer template tag, conflicts land in files the adopter edited for branding, demo dashboard, or config — mixed together with template wiring they never intended to own.

This makes template upgrades painful and unpredictable. Release notes cannot reliably say "these paths are safe" vs "expect conflicts here." The rename script walks the entire repo and leaves template code mutated, which further drifts forks from upstream.

What we want: a clear ownership model so fork owners can:

  1. Customize their adopter app (one app per fork, web + mobile) in a single predictable tree
  2. Merge upstream template updates with conflicts concentrated in template/ and packages/, not in their app code
  3. Rebrand via npm run rename without touching template code at all
  4. Add adopter-specific database schema, integration tests, and pgTAP tests without fighting template migrations

This directly supports the product promise in README: "Fork the template; update the packages."


Expected adopter workflow (after this work)

Adopter customizes Lives in
Branding, logo, colors, legal adopter/config/, adopter/assets/
Landing copy + marketing images adopter/config/landing.ts, adopter/assets/landing/
Plan tiers / feature keys adopter/config/billing.ts
Policies, waitlist copy, email personalization adopter/content/, adopter/config/, adopter/email/
Dashboard, custom pages/components (web + mobile) adopter/web/, adopter/mobile/
Adopter database schema adopter/db/migrations/
Adopter tests (unit, integration, db, e2e) adopter/**/__tests__/, adopter/tests/, adopter/e2e/
Adopter rarely edits Lives in
Admin, billing shells, auth pages, landing section components apps/*/src/template/
Framework packages packages/ (@beakerstack/*)
Template migrations, CI, setup scripts supabase/migrations/, .github/, scripts/

Typical upgrade after reorg:

git merge 2026.NNN
# conflicts in apps/*/src/template/** and infra — not in adopter/
npm install
supabase db reset && npm run db:apply-adopter
npm run type-check && npm run test

Naming model

Three tiers — avoid confusing "product" (billing tiers) with "adopter app":

Tier Form Where
Template scope @beakerstack/* packages/, imports in apps — stays in template code
Demo product name Beaker Stack, logo, landing copy/images adopter/ only
Demo app identifiers beakerstack, productId: 'beakerstack' adopter/config/ only — what npm run rename rewrites

Template code uses neutral local names (billingConfig, waitlistConfig). No beakerstack* file/export/class names outside adopter/.


Proposed layout (summary)

adopter/                         # Entire tree = adopter-owned
  config/                        # branding, legal, colors, landing, billing, waitlist
  content/                       # policy markdown
  assets/                        # icon, landing images
  email/                         # .personalization.json
  web/                           # pages, components, routes.tsx
  mobile/                        # screens, components, navigation.tsx
  db/migrations/                 # adopter schema (never supabase/migrations/)
  tests/integration/             # adopter Jest integration tests
  tests/db/                      # adopter pgTAP tests
  e2e/web/specs/, e2e/mobile/flows/

apps/web/src/template/           # admin, billing wiring, landing components, auth pages
apps/mobile/src/template/        # mirror template wiring

Key structural changes:

  1. configureAdopter() bootstrap — extract branding/legal/colors/policies from @beakerstack/shared into adopter/config/; shared package reads injected config at runtime
  2. Route/screen registriesadopter/web/routes.tsx and adopter/mobile/navigation.tsx imported by thin App.tsx / AppNavigator.tsx
  3. Adopter-only renamenpm run rename scans only adopter/**; template code stays byte-identical to upstream
  4. Adopter DB workflowsupabase db reset (template) → npm run db:apply-adopter (adopter migrations on top)
  5. Test split — template tests in packages/, template/**, tests/integration/, supabase/tests/; adopter tests under adopter/

Acceptance criteria

Layout & ownership

  • adopter/ directory exists with documented zone map in docs/CUSTOMIZING.md
  • All adopter config (branding, legal, colors, landing, billing, waitlist, policies, landing images) lives under adopter/
  • Template wiring lives under apps/web/src/template/ and apps/mobile/src/template/
  • Demo dashboard (web + mobile) lives under adopter/web/ and adopter/mobile/ as replaceable starter
  • App.tsx and AppNavigator.tsx import adopter routes/screens via stable registry modules

Naming & rename

  • Template code uses neutral exports (billingConfig, waitlistConfig); demo productId: 'beakerstack' and displayName: 'Beaker Stack' live only in adopter/config/
  • @beakerstack/* npm scope unchanged in template code
  • npm run rename scopes exclusively to adopter/** — no changes to apps/, packages/, supabase/, scripts/, docs/, .github/
  • docs/renaming.md updated for adopter-only rename model

Database

  • adopter/db/migrations/ established; adopter schema never added to supabase/migrations/
  • npm run db:apply-adopter applies adopter migrations after template reset
  • npm run migration:new:adopter scaffolds adopter migration files
  • Adopter DB types generated to adopter/db/types/ separately from template types

Tests

  • Adopter unit tests co-located under adopter/**/__tests__/
  • Template unit tests under apps/*/src/template/**/__tests__/ and packages/*
  • Template integration tests remain in tests/integration/
  • Adopter integration tests in adopter/tests/integration/ with test:integration:adopter
  • Template pgTAP in supabase/tests/; adopter pgTAP in adopter/tests/db/ with test:db:adopter
  • Marketing/copy E2E in adopter/e2e/; functional flow E2E stays in tests/e2e/web/specs/
  • Mock adopter config fixture for template/framework tests (no direct import of live adopter/config/branding.ts)
  • docs/TESTING.md decision matrix updated

Upgrade ergonomics

  • docs/UPGRADING.md documents conflict resolution: keep adopter/**, accept upstream for template/** and packages/**
  • Promotion PR adopter notes template lists likely conflict paths by zone
  • Optional: .gitattributes adopter/** merge=ours documented
  • Optional: npm run upgrade:check script

Quality gates

  • All existing tests pass after migration
  • npm run type-check passes
  • CI runs full suite (template + adopter); documents fork CI adopter-only slice

Phased rollout

Phase 1 — Document + physical moves (low risk)

  • Create adopter/ tree; move config files, landing images
  • Add apps/*/src/template/; move admin + billing wiring
  • Add docs/CUSTOMIZING.md
  • Begin test moves (adopter config tests → adopter/config/__tests__/)

Phase 2 — Shared decoupling + naming (medium)

  • Extract branding/legal/colors/policies from @beakerstack/shared
  • Add configureAdopter() bootstrap
  • Neutral export names; rewrite rename script (adopter-only)
  • Move dashboard demo to adopter/web + adopter/mobile

Phase 3 — Upgrade ergonomics + test/DB completion (polish)

  • Route/screen registries, .gitattributes, release notes automation
  • adopter/e2e/, adopter/tests/integration/, adopter/tests/db/
  • db:apply-adopter, migration:new:adopter, adopter test scripts

Success criteria (outcome)

After shipping, a fork owner who has customized branding, dashboard, and added adopter DB schema can merge a CalVer tag with:

  • Zero conflicts in adopter/** (or auto-preserved via merge driver)
  • Conflicts only in apps/*/src/template/**, supabase/migrations/, and infra they expect to review
  • Rebrand without any template code diff
  • Day-to-day CI via adopter test slice; full template suite after upgrades

Related docs

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Planning

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions