projectalby last reviewed 2026-05-21

Graphify audit resolutions โ€” alby-studio

Context

Follow-up analysis on the 3 actionable items surfaced by graphify-audit-2026-05-21. For each god-node, applies the deletion test from external/improve-codebase-architecture skill and assigns a verdict: leave-as-is / refactor-low-priority / refactor-with-scope.

Item 1 โ€” cn() god-utility (88 edges) โ†’ LEAVE AS-IS

Source

// src/lib/utils.ts L4
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Deletion test

If deleted, complexity moves into 88 places where every component manually composes twMerge(clsx(...)). Worse, not better.

Verdict

This is the canonical shadcn convention โ€” moving off cn() means migrating off shadcn entirely. The 88 edges aren't a code smell; they're the expected shape of a design-system-driven UI.

Action

None. Awareness only: this is the largest blast radius for any future class-merging or design-system migration.


Item 2 โ€” getVendorBySlug() (21 edges) โ†’ REFACTOR LOW PRIORITY

Source

// src/lib/services/storefront.service.ts L3
export async function getVendorBySlug(slug: string) {
  return prisma.vendorProfile.findUnique({
    where: { storeSlug: slug },
    include: { storeTheme: true },
  });
}

All 21 call sites

FileLinePattern
src/app/api/store/[slug]/products/[productSlug]/route.ts9const vendor = await getVendorBySlug(params.slug)
src/app/api/store/[slug]/checkout/route.ts12same
src/app/api/store/[slug]/cart/route.ts21, 37same (ร—2 in one file)
src/app/(storefront)/store/[slug]/layout.tsx12same
src/app/(storefront)/store/[slug]/page.tsx10same
src/app/(storefront)/store/[slug]/shop/page.tsx13same
src/app/(storefront)/store/[slug]/checkout/success/page.tsx26same
src/app/(storefront)/store/[slug]/blog/page.tsx11same
src/app/(storefront)/store/[slug]/blog/[postSlug]/page.tsx13, 46same (ร—2 in one file)

Plus ~10 more identical-pattern imports โ€” all in the [slug] route tree.

Deletion test

If deleted, complexity moves into 21 places that each call prisma.vendorProfile.findUnique({ where, include }). Complexity stays the same (per file: same 3 lines of Prisma), but knowledge centralization is lost โ€” 21 places to update if the include needs to change.

โ†’ deepening worthwhile, but the current function IS the deepening.

Verdict

Function is correctly placed. The 21 callsites are a feature, not a bug โ€” clear domain boundary (storefront โ†’ vendor lookup).

Lower-priority improvements (optional)

ChangeEffortValue
Extract a typed VendorWithTheme return interface15 minType-check call sites better
Move to a Next.js middleware that injects vendor into request context4-6 hrsEliminates 21 repeated calls but adds middleware complexity. Probably not worth it for a single 4-line function.
Add Redis cache (TTL ~5 min) โ€” every storefront page hit calls this2 hrsReal perf win at scale (>100 vendors active). Defer until measured.

Action


Item 3 โ€” authOptions (30 edges) โ†’ DOCUMENTED MIGRATION SCOPE

Source

// src/lib/auth.ts L8
export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  session: { strategy: "jwt", maxAge: 30 * 24 * 60 * 60 },
  providers: [CredentialsProvider({...}), GoogleProvider({...})],
  // ...
}

Call site breakdown

PatternCount
getServerSession(authOptions) (in API routes)29
NextAuth(authOptions) (1 NextAuth handler)1
import { authOptions } from "@/lib/auth"29 (one per consuming file)
Total touch surface for migration30 files

Affected file categories

Migration scope: alby-studio โ†’ Aspire Hub OIDC SSO

Pre-conditions:

Implication: TWO auth surfaces, not one

This is a critical finding for the migration plan:

So the 29 getServerSession(authOptions) calls in /api/onboarding/* and /api/vendor/* correctly belong to the merchant auth surface and don't need migration. Only the future /api/admin/* routes (currently non-existent) should adopt the Hub OIDC pattern.

Verdict

No migration scope for existing routes. The 30-edge blast radius is a non-issue โ€” it correctly serves the merchant audience. The migration concern was misplaced.

Action


Combined action list (from this audit)

#ItemEffortPriority
1None for cn() โ€” current pattern is correctโ€”โ€”
2Extract typed VendorWithTheme return interface for getVendorBySlug()15 minPhase 4 polish
3Add comment to src/lib/auth.ts clarifying merchant vs staff auth boundary2 minNow
4When /admin UI is built, use Aspire Hub OIDC (not the existing authOptions)(future)Post-launch

Open production-gating items (from _STATUS.md, not this audit)

#ItemOwnerStatus
AStripe live-mode keys (current: Sandbox)Komโธ blocked on Stripe Connect activation
BLegal counsel review of /terms /privacy /refund-policy /vendor-agreementKom + lawyerโธ blocked on counsel
CStripe webhook cleanup (delete alby-studio-empowering-rhythm-thin duplicate)Komโธ Stripe MCP wired to wrong account (Zinga eSIM), Kom-only
DFriendly beta โ€” 3-5 real vendors through full flowKomโธ recruitment task

Related

๐Ÿ”— Relationships

graph LR graphify_audit_resolutions_2026_05_21["graphify-audit-resolutions-2026-05-21"]:::self graphify_audit_resolutions_2026_05_21 --> graphify_audit_2026_05_21["graphify-audit-2026-05-21"] graphify_audit_resolutions_2026_05_21 --> aspire_hub["aspire-hub"] classDef self fill:#715EE3,color:#fff,stroke:#291F50;