~kris/dots

srice

srice/doc/files/claudeAudit.md -rw-r--r-- 15.7 KiB
e98f3b03 — Kris Yotam chore: sync local state after restore (push updates, no pull) a month ago

#Claude Structural Audit: krisyotam.com

Date: 2026-05-19 Scope: Full codebase structural analysis (875 source files, ~51K lines) Goal: Identify structural issues blocking the August lean-codebase target


#Executive Summary

The site is a well-built Next.js 16 app with solid fundamentals (strict TypeScript, no circular deps, consistent imports, proper SQL parameterization). The main structural problems are:

  1. Monolithic files - 20+ files over 300 lines, 2 over 1000 lines
  2. Copy-paste route patterns - tracking routes (anime/film/tv/manga/games) are near-identical
  3. Mixed database paradigms - SQLite everywhere except analytics (Neon Postgres) and one rogue singleton
  4. 31 dead exports in lib/ never imported anywhere
  5. No error boundaries - zero error.tsx files in the entire app
  6. Font bloat - 16MB of fonts across 10+ families, many likely unused

#1. App Router Structure

Stats: 79 pages, 27 API routes, 8 route groups

#1.1 Empty Route Groups (delete or populate)

Group Status
(info) Completely empty
(media) Completely empty
(updates) Has layout.tsx + CSS but zero routes

#1.2 Copy-Paste Tracking Routes

The tracking section has 5 near-identical page patterns:

  • (tracking)/anime/ + anime/watched/
  • (tracking)/film/ + film/watched/
  • (tracking)/tv/ + tv/watched/
  • (tracking)/games/ + games/played/
  • (tracking)/manga/ + manga/read/

Each has its own layout.tsx (12 lines, identical structure), its own API route, its own client component. The pages differ only in data source and field names. This is the single biggest structural redundancy.

Fix: Generic (tracking)/[medium]/ dynamic route + shared layout + consolidated API at /api/tracking/[medium]/.

#1.3 Duplicate Navigation Routes

  • /categories (nav group) vs /(content)/[type]/categories (per-content-type)
  • /tags (nav group) vs /(content)/[type]/tags
  • sequences/categories/page.tsx (58 lines) vs sequences/category/page.tsx (26 lines, empty placeholder)

#1.4 Oversized Route Files (>300 lines)

File Lines Issue
api/system/utils/route.ts 762 Kitchen-sink endpoint, needs splitting
(misc)/stats/client.tsx 729 Heavy viz logic mixed with UI
(misc)/colophon/client.tsx 520 Could extract sections
(misc)/guestbook/client.tsx 474 Form + display + interactions
api/tracking/reading/route.ts 449 Complex aggregation
(content)/sequences/client.tsx 387 Sequence browser
(nav)/search/client.tsx 351 Search interface
(misc)/contact/client.tsx 347 Contact form
(tracking)/globe/client.tsx 345 Map viz
api/reference/route.ts 318 Reference data
(misc)/symbols/client.tsx 305 Symbol display

#1.5 7 Identical Tracking Layouts

These are copy-paste identical (import CSS, wrap in <div className="py-8">):

  • (tracking)/anime/layout.tsx
  • (tracking)/film/layout.tsx
  • (tracking)/tv/layout.tsx
  • (tracking)/manga/layout.tsx
  • (tracking)/games/layout.tsx
  • (tracking)/globe/layout.tsx
  • (tracking)/reading/layout.tsx

Fix: Single (tracking)/layout.tsx with shared styles.

#1.6 API Route Redundancy

Tracking API routes follow identical patterns:

  • /api/tracking/anime/route.ts (166 lines)
  • /api/tracking/film/route.ts (150 lines)
  • /api/tracking/tv/route.ts (187 lines)
  • /api/tracking/manga/route.ts (169 lines)
  • /api/tracking/media/route.ts (146 lines)

All query media-db.ts with slightly different table names. Could be one parameterized route.


#2. Components

Stats: 152 files, 23,057 lines, 25 directories

#2.1 Monolithic Components (must split)

File Lines What's Inside
media/reading/reading.tsx 1,589 9+ distinct sub-components, data fetching, state, rendering
interactive/survey.tsx 1,220 Form state, validation, conditional rendering, answer tracking
content/404-block.tsx 756 Multiple error page patterns
content/sequence.tsx 686 Parsing + rendering + styling + fetch
seo/print.tsx 602 Print layout + styling logic
content/verse.tsx 584 Verse display + formatting
interactive/popups.tsx 531 Modal manager

#2.2 Data Fetching in UI Components (19 files)

These components mix presentation with data fetching -- should accept data as props or use server components:

  • media/reading/reading.tsx - fetches reading data
  • content/sequence.tsx - fetches code sequences
  • content/404-block.tsx - fetches error data
  • content/feed-shared.tsx - fetches feed updates
  • content/scripts.tsx - fetches script content
  • content/graph.tsx - fetches graph data
  • interactive/popups.tsx - fetches popup data
  • layout/header.tsx - fetches header content
  • layout/footer.tsx - fetches footer data
  • home/about/InterestingPeople.tsx - fetches people data
  • home/HomeGitHubContributions.tsx - GitHub API calls
  • home/ListHeader.tsx - fetches quote data
  • nav/search-overlay.tsx - search API calls
  • nav/settings-panel.tsx - loads settings
  • pages/quotes/infiniteMovingQuotes.tsx - fetches quotes
  • pages/quotes/quoteOfTheDay.tsx - fetches daily quote
  • home/about/WordOfTheDay.tsx - fetches daily word
  • home/about/Favorites.tsx - loads favorites
  • typography/1611bible.tsx - fetches bible data

#2.3 Overlapping Components

10 card variants:

  • ui/card.tsx, ui/glowCard.tsx, ui/glowCardGrid.tsx, ui/hover-card.tsx
  • content/PaginatedCardGrid.tsx, content/mediaCard.tsx
  • pages/books/book-card.tsx, pages/predictions/prediction-card.tsx
  • typography/quoteCard.tsx, home/PoetryCard.tsx

3 header variants:

  • layout/header.tsx (432 lines)
  • layout/oc-header.tsx (224 lines)
  • home/HomeHeader.tsx (114 lines)

3 footer variants:

  • layout/footer.tsx (233 lines)
  • layout/global-footer.tsx (92 lines)
  • typography/expanded-footer-block.tsx (162 lines)

4 quote components:

  • pages/quotes/quoteOfTheDay.tsx
  • pages/quotes/quotesFeed.tsx
  • pages/quotes/infiniteMovingQuotes.tsx
  • typography/quoteCard.tsx

2 duplicate named files:

  • typography/footnotes.tsx (58 lines) vs content/footnotes.tsx (141 lines)
  • typography/table.tsx vs content/table.tsx (284 lines)

#2.4 Naming Inconsistency

Mixed PascalCase and kebab-case within same directories:

  • home/ - featured-post.tsx alongside HomeHeader.tsx
  • home/about/ - name-breakdown.tsx alongside InterestingPeople.tsx
  • nav/ - search-overlay.tsx alongside NeighborsOverlay.tsx

Convention from CLAUDE.md says camelCase for component filenames, but neither PascalCase nor kebab-case matches that.

#2.5 Misplaced Files

  • contribution-graph.tsx sits at components root, belongs in home/
  • rolls/ directory has a single file (326 lines), belongs in interactive/ or pages/
  • changelog/ directory exists but is empty
  • media/anime/ directory exists but may be empty

#3. Lib / Data Layer

Stats: 38 files in lib/, 7,468 lines total

#3.1 Dead Exports (31 functions never imported)

date.ts (10 dead):

  • formatRelative, formatInCentralTime, getCurrentMonthYear, getCurrentYear
  • isPast, isFuture, isToday, formatDateCompact, formatDateWithValidation, isValidDate

lab-db.ts (6 dead):

  • getSurveyResponses, getAllSurveyResponses, getSurveyResponseCount
  • getAllSurveys, upsertSurvey, syncSurveysFromFiles

doc.ts (3 dead):

  • getDocumentsUnderPath, getDocBySlug, getTopLevelDirectories

data.ts (2 dead):

  • getVerseByTypeAndSlug, getDocumentFilePath

mdx.ts (3 dead):

  • fileExists, listMdxFiles, extractHeadingsWithHierarchy

Other (7 dead):

  • analytics-db.ts: getHistoryTimeline
  • canonical-url.ts: _resetCanonicalUrlCache
  • cookie-sign.ts: signCookie
  • form-parser.ts: extractFormFrontmatter
  • guestbook-db.ts: getGuestbookEntry
  • media-db.ts: getMusicPlaylists
  • prompts.ts: getPromptsByCategory, getPromptBySlug

#3.2 Duplicate Logic

getMovies() defined twice:

  • media-db.ts:getMovies() - direct SQLite query
  • film-utils.ts:getMovies() - async wrapper that calls the above + transforms field names

TilEntry/NowEntry defined twice:

  • data.ts (without id field)
  • system-db.ts (with id: number)

Date formatting redundancy:

  • 14 date formatting functions, many overlapping (formatCompact vs formatDateCompact, formatISO vs formatYMD, formatRange vs formatDateRange)

#3.3 Inconsistent Database Patterns

Pattern Tech Files Issue
withDb() wrapper SQLite 108 uses Good, canonical
Neon serverless Postgres analytics-db.ts only Different paradigm, async vs sync
Manual singleton SQLite reference-db.ts only Reinvents withDb(), connection never closed

Fix: Migrate reference-db.ts to use withDb(). Decide analytics future (keep Postgres or migrate to SQLite).

#3.4 Oversized Lib Files

File Lines Exports Recommendation
media-db.ts 837 91 Split by domain (films, games, anime, reading)
data.ts 713 36 OK as-is, well-organized
system-db.ts 620 30 Split: metadata, quotes, supporters
analytics-db.ts 504 30 OK, cohesive domain
survey-parser.ts 345 - OK, single responsibility
seo.ts 320 5 Split: JSON-LD, feeds, OG

#3.5 Types Scattered

Type definitions live in 4+ locations:

  • src/lib/types/content.ts (264 lines)
  • src/lib/types/media.ts (58 lines)
  • Inlined in analytics-db.ts, media-db.ts, system-db.ts, reference-db.ts

No single source of truth for types.


#4. Config / Build

#4.1 Font Bloat

16MB of fonts in public/fonts/ across 10+ families (53 .woff2 files):

  • Noto Serif: 1.6MB (largest)
  • Plus: IBM Plex Sans, Inter, Source Serif 4, Playfair Display, Fraunces, IBM Plex Mono, Outfit, Cormorant Garamond, Crimson Pro, Literata, Newsreader, Source Serif Pro

Many are likely unused or used on a single page. Audit needed.

#4.2 Build Safety Gaps

  • build:lowmem disables both type-checking (NEXT_DISABLE_TYPE_CHECKING=1) and ESLint (DISABLE_ESLINT_PLUGIN=true)
  • No ESLint config file exists (relying on Next.js defaults)
  • No Prettier config (no formatting standard)
  • No test files despite Vitest config present

#4.3 CSS Organization

Hybrid approach (all acceptable, no conflicts):

  • Tailwind (primary)
  • 17 plain CSS files for specific components/routes
  • globals.css at 48K is large with excessive !important in code block styles
  • 96+ CSS variables from the pastel color system (12 colors x 8 variants)

#4.4 Database Sizes

Database Size Notes
reference.db 59MB Largest by far, contains poetry/reference data
content.db 8.7MB Main content
system.db 2.4MB Metadata, quotes, TIL
media.db 656KB Films, anime, games

All intentionally committed to git via !data/*.db gitignore exception.


#5. Code Quality

#5.1 Security Issues

Cookie secret default (CRITICAL):

  • cookie-sign.ts:3 has hardcoded fallback: 'krisyotam-cookie-secret-change-in-prod'
  • Should throw if COOKIE_SECRET not set in production

TikZ XSS risk (MEDIUM):

  • typography/tikz.tsx renders SVG via dangerouslySetInnerHTML from server API
  • No sanitization library (DOMPurify) applied to SVG output

CSRF localhost bypass:

  • csrf.ts allows localhost:3000 and localhost:3080 without environment check

#5.2 No Error Boundaries

Zero error.tsx files in the entire app router. A crash in any route shows the default Next.js error page with no recovery path.

#5.3 TODO/FIXME Comments

Only 3 found (minimal debt):

  • surveys/[slug]/client.tsx:40 - // TODO: Submit to Supabase
  • typography/tweet.css:53,59 - // TODO: figure out a way to reuse this with a.tsx

#5.4 Hardcoded URLs

100+ instances of krisyotam.com hardcoded across the codebase. Acceptable for a personal site but blocks any future domain changes.

#5.5 Duplicated Patterns

URL replacement logic (2 files, identical):

  • typography/citation.tsx:28-30
  • seo/print.tsx:35-36
  • Both do localhost-to-production URL conversion

Image unoptimized check (7 files, identical):

  • Pattern: unoptimized={image?.includes('krisyotam.com')}
  • Repeated in 7 component files

#5.6 Console Statements

40+ console.error calls across production code. Most are legitimate error logging but several in components should be removed or replaced with proper error handling.


#6. Structural Scorecard

Area Grade Key Issue
Route organization B- Empty groups, tracking duplication
Component architecture C+ 2 monoliths, 19 files with data fetching in UI
Lib/data layer B 31 dead exports, 3 DB paradigms
Type system B- Scattered definitions, duplicates
Config/build B Font bloat, no linting enforcement
Security B- Cookie fallback, no error boundaries
Code hygiene B+ Minimal TODOs, consistent imports, no circular deps
Overall B- Solid foundation, needs consolidation

#7. Priority Actions for August Target

#Tier 1: High Impact, Moderate Effort

  1. Consolidate tracking routes - Replace 5 copy-paste route sets + 7 layouts + 5 API routes with 1 dynamic route, 1 layout, 1 API
  2. Split reading.tsx (1,589 lines) into sub-components + hooks
  3. Split survey.tsx (1,220 lines) into form logic + presentation
  4. Split api/system/utils (762 lines) into discrete endpoints
  5. Delete 31 dead exports from lib/

#Tier 2: Medium Impact

  1. Delete empty route groups ((info), (media), (updates))
  2. Migrate reference-db.ts to use withDb() pattern
  3. Split media-db.ts (837 lines) by domain
  4. Consolidate duplicate types (TilEntry, NowEntry, Movie)
  5. Add error.tsx boundaries to critical route groups
  6. Audit fonts - identify unused families, subset the rest
  7. Fix cookie-sign.ts - remove hardcoded fallback secret

#Tier 3: Polish

  1. Standardize component naming to camelCase per project convention
  2. Consolidate date formatters from 14 to ~5
  3. Extract URL replacement to shared utility
  4. Clean up globals.css - remove !important overrides
  5. Move contribution-graph.tsx to home/
  6. Delete empty directories (changelog/, media/anime/)
  7. Extract image unoptimized check to utility

#8. Files by Refactoring Priority

#Must Touch (Tier 1)

src/components/media/reading/reading.tsx        (1,589 lines)
src/components/interactive/survey.tsx            (1,220 lines)
src/app/api/system/utils/route.ts               (762 lines)
src/app/(tracking)/*/layout.tsx                  (7 identical files)
src/app/(tracking)/*/page.tsx                    (5 near-identical patterns)
src/app/api/tracking/*/route.ts                  (5 near-identical APIs)

#Should Touch (Tier 2)

src/lib/media-db.ts                             (837 lines, 91 exports)
src/lib/system-db.ts                            (620 lines)
src/lib/reference-db.ts                         (rogue singleton pattern)
src/lib/date.ts                                 (14 formatters, 10 dead)
src/lib/types/                                  (scattered across 4+ locations)
src/lib/cookie-sign.ts                          (security fix)

#Nice to Touch (Tier 3)

src/components/content/404-block.tsx             (756 lines)
src/components/content/sequence.tsx              (686 lines)
src/components/seo/print.tsx                     (602 lines)
src/components/content/verse.tsx                 (584 lines)
src/components/interactive/popups.tsx            (531 lines)
src/app/globals.css                              (48K, !important abuse)