PR1 audit: server-code sharing scope for `apps/studio` split

PR1 audit: server-code sharing scope for apps/studio split

Classifies the 9 apps/web/src/server/* files the studio split plan
(.agents/plans/2026-08-19-split-studio-app.md) lists as studio-shared, per
that plan’s PR1 scope. (a) = already a thin createServerFn wrapper around
package logic, studio writes its own thin wrapper importing the package
directly. (b) = real logic embedded in apps/web/src/server, needs
extraction to a package first.

(b) — needs extraction

  • server/auth/handlers.ts (443 lines). handleGetSession is the one
    function every other file’s requireUser() helper depends on (billing-fn,
    delete-account-fn, redeem-fn, agencies-gate all reimplement the identical
    requireUser by calling it). It plus session.ts’s
    readSessionCookie/verifySessionToken need to move to a shared package
    (e.g. a new session-check.ts in @brioso/auth-core, which already owns
    otp/rate-limit) so both apps call one real session-check instead of
    duplicating cookie/token logic. The rest of the file (OTP issue/verify,
    login/logout) is web-only — marketing’s login flow, not studio’s — and
    stays in apps/web.
  • server/account/delete-account.ts (106 lines, sibling of the audited
    delete-account-fn.ts wrapper, not itself in the named 9 but is the real
    logic backing it). DB transaction + Stripe cancel — extract to a package
    (e.g. @brioso/billing already owns Stripe checkout/portal — this fits
    there, or a new @brioso/account if billing shouldn’t own deletion).
    delete-account-fn.ts itself is (a) once this + auth extraction land.
  • server/catalog/queries.ts (395 lines) — but studio uses only one
    type import
    (CatalogTrackRowWithPrompt, via InspirationTracks.tsx)
    and, per the file’s own comment, listRandomPublished() (studio’s
    inspiration rail). Grepped studio’s actual imports — confirmed no other
    function from this file is reached from components/studio/*,
    routes/studio/*, routes/_authed/studio/*, or lib/studio/*. The other
    6 exports (listPublishedByGenre/Mood/Subgenre/Instrument/UseCase,
    listPublishedLeafCounts, listPublishedTaxonomyLeaves) back marketing’s
    genre/mood/subgenre pages and sitemap — never touched by studio. Do not
    move the whole file.
    Extract only listRandomPublished +
    CatalogTrackRowWithPrompt + getTrackBySlug (its one dependency chain)
    to a shared package — likely packages/catalog (currently pure
    taxonomy/domain, no DB access; this would be its first DB-touching export,
    flag that as a package-shape change worth a second look) or a new
    packages/catalog-queries. Leave the rest in apps/web.

(a) — already thin, write parallel wrappers

  • server/billing/billing-fn.ts — delegates to @brioso/billing/*
    directly; only couples to handlers.ts via requireUser. Thin once auth
    extraction lands.
  • server/db/client.ts (24 lines) — trivial @brioso/db +
    @brioso/config wiring, no package needed. apps/studio writes its own
    copy verbatim (app-local, not shared logic).
  • server/jobs/playback-token-fn.ts / share-kit-link-fn.ts — both
    10-line passthroughs. Their real logic (playback-token.ts 5 lines,
    share-kit-link.ts 8 lines — not in the named 9, checked directly) is
    already trivial; no extraction needed, studio can either import a moved
    copy or these can be promoted into @brioso/jobs outright given how small
    they are.
  • server/entitlements/redeem-fn.ts — delegates to
    @brioso/entitlements/redeem directly; only couples to handlers.ts via
    requireUser.
  • server/flags/agencies-gate.ts — delegates to @brioso/obs-js’s
    flag(); couples to handlers.ts via requireUser and to
    ~/server/obs/flagSubject.ts (42 lines, not in the named 9 — small, check
    at PR4 time whether it needs to move too or studio can duplicate it).

Verdict for PR4

Not “new packages/server-shared” — narrower: extend @brioso/auth-core
with the session-check (handles 5 of 9 files’ only real coupling), extract
delete-account.ts’s transaction logic to @brioso/billing (or a new
small package), and pull 3 functions (not the whole file) out of
catalog/queries.ts. Everything else is parallel thin wrappers, no new
package.