# 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 `
`): - `(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 6. **Delete empty route groups** (`(info)`, `(media)`, `(updates)`) 7. **Migrate reference-db.ts** to use `withDb()` pattern 8. **Split media-db.ts** (837 lines) by domain 9. **Consolidate duplicate types** (TilEntry, NowEntry, Movie) 10. **Add error.tsx** boundaries to critical route groups 11. **Audit fonts** - identify unused families, subset the rest 12. **Fix cookie-sign.ts** - remove hardcoded fallback secret ### Tier 3: Polish 13. **Standardize component naming** to camelCase per project convention 14. **Consolidate date formatters** from 14 to ~5 15. **Extract URL replacement** to shared utility 16. **Clean up globals.css** - remove `!important` overrides 17. **Move contribution-graph.tsx** to `home/` 18. **Delete empty directories** (changelog/, media/anime/) 19. **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) ```