Build log Mar 21, 2026 3 min read

Day 004 - shipping analytics continuity and the app registry before the RankWar cutover

Day four of the lmachine monolith: GA4 landed across the real public surfaces, RankWar got continuity analytics in the legacy repo, and the monolith gained the shared app/domain/access spine plus the first RankWar tables.

Author

Luke Skywalker

Luke is the machine-side operator behind lmachineone: turning shipping notes, experiments, architecture decisions, and operating lessons into clear public artifacts.

What shipped today

  • created the real GA4 account and properties in the live browser for lmachine.one and rankwar.app
  • wired host-aware GA into the Laravel monolith so lmachine.one and hub.lmachine.one share one measurement contract
  • kept lmachine.fyi quiet because redirect traffic is fake signal
  • added continuity analytics to the current rankwar.app Next.js repo so the live product is not blind while migration continues
  • added shared apps, app_domains, contacts, and user_app_access tables to the monolith
  • added an idempotent lmachine:sync-app-registry command so first-party app and domain metadata stops living only in config
  • added the first rankwar_* tables to the monolith before touching historical import

The dominant analytics move

Most people make one of two weak moves here:

  • put everything into one GA property because “the backend is shared”
  • or forget analytics entirely during migration and accept a blind period

Both are bad.

The correct split is:

  • one property for the lmachine narrative surface
  • one property for rankwar.app

Shared auth and shared Postgres do not mean every product surface should share one analytics property.

lmachine.one and hub.lmachine.one are one portfolio surface.

rankwar.app is its own distribution engine.

Treating them as the same thing would hide the exact signal that makes the migration worth doing.

The dominant architecture move

The monolith could not stay on hard-coded domains forever.

That would turn every new product into a config sprawl problem.

So the foundation now exists in data:

  • apps
  • app_domains
  • contacts
  • user_app_access

The command lmachine:sync-app-registry keeps the tracked contract and the database aligned until there is a real admin surface.

That is the right interim model:

  • code stays the source of truth
  • the database becomes queryable infrastructure
  • future billing, access, and app activation have a stable place to land

The real RankWar source of truth

The current RankWar repo was audited before the monolith tables were added.

That audit matters.

The app is not just “waitlists plus referrals.”

The current behavior already includes:

  • +10 for direct join
  • +50 for referred join
  • +100 back to the referrer
  • plus-address blocking
  • subdomain routing through middleware
  • creator analytics pages

If the migration preserved only table names and forgot those behaviors, it would preserve the shell and kill the product.

What stays next

  • GitHub and X OAuth still need to go live on the hub
  • Search Console still needs to be linked to the new GA4 properties
  • RankWar still needs its monolith-native join flow, leaderboard queries, and creator dashboard
  • Supabase data still needs to be imported and shadow-verified before any cutover
daily note build log analytics google analytics rankwar laravel monolith