# Codex Structural Audit Generated: 2026-05-20 Scope: structural issues in `krisyotam.com`, with emphasis on making the codebase leaner, easier to navigate, and safer to change by August. This audit does not cover visual design polish or content quality. ## Executive Summary The main problem is not one bad subsystem. The project has several parallel sources of truth that have drifted: - Content type definitions are repeated across routing, sync scripts, feeds, sitemap, search, SEO, and tests. - SQLite path ownership is split between root `data/` and stale `public/data/` artifacts. - URL resolution exists in several forms: Next rewrites, canonical helpers, component fallbacks, sitemap, JSON-LD, and script utilities. - `src/app` mixes route modules with content payloads. - Client boundaries are broader than they need to be, especially around layout and listing pages. - `public/scripts` contains both browser assets and private operational tooling. - Large CSS and data modules have become catch-all files. The highest-value August target is: 1. One content registry. 2. One database root. 3. One canonical URL resolver. 4. One script path helper. 5. Route folders that only contain route code. 6. Shared listing and tracking primitives instead of repeated page clients. ## Severity 1: Data and Routing Sources of Truth ### 1. Content type lists are duplicated and inconsistent Evidence: - `src/app/(content)/[type]/config.ts` - `next.config.mjs` - `src/lib/seo.ts` - `src/lib/content.ts` - `src/lib/graph.ts` - `src/app/sitemap.ts` - `src/app/api/content/search/route.ts` - `src/app/api/content/md/[slug]/route.ts` - `src/app/api/system/utils/route.ts` - `public/scripts/dev/syncContent.js` - `public/scripts/dev/slugCollisions.js` - `public/scripts/prod/magic-urls.js` - `public/scripts/dev/generateMetadata.js` Why it matters: Adding or changing a content type requires edits in many unrelated files. Some lists include `documents`, `til`, `now`, `scripts`, `notebooks`, or `sequences`; others exclude them. That makes behavior hard to predict across feeds, search, sitemap, sexy URLs, and sync. Fix: Create a single registry that defines each content surface: - table name - public route - canonical URL mode - sync source - date column - category column - tag relation - ratings support - feed/search/sitemap inclusion - MDX import directory Suggested file: - `src/config/contentRegistry.ts` for app/runtime code. - `scripts/lib/contentRegistry.mjs` or generated JSON for CommonJS scripts and `next.config.mjs`. First batch: 1. Introduce the registry with current behavior only. 2. Convert read-only consumers: `src/lib/seo.ts`, `src/lib/content.ts`, `src/app/sitemap.ts`, `src/app/api/content/search/route.ts`. 3. Convert write/sync scripts after the registry is proven. ### 2. Database artifact ownership is split Evidence: - Runtime defaults use root `data/` via `src/lib/db.ts`. - Root `data/*.db` is explicitly tracked by `.gitignore`. - `public/data/content.db`, `public/data/media.db`, and `public/data/system.db` exist as zero-byte files. - `public/scripts/doc/syncDocs.js` resolves `../../data/content.db` from inside `public/scripts/doc`, which lands at `public/data/content.db`. - `public/scripts/dev/syncNotebooks.js` has the same public-data path issue. - `public/scripts/dev/blogActivity.js` and `public/scripts/prod/magic-urls.js` still reference public DB paths. Why it matters: The app reads one database root while some scripts mutate or inspect another. This creates silent drift and makes script output untrustworthy. It also keeps fake DB files under a web-served directory. Fix: Make root `data/` the only SQLite root. Add a shared script helper: - `scripts/lib/paths.mjs` - exports `PROJECT_ROOT`, `DATA_DIR`, `CONTENT_DIR`, and `dbPath(name)` - honors `KRISYOTAM_DATA_DIR` and `KRISYOTAM_CONTENT_DIR` First batch: 1. Update `syncDocs.js`, `syncNotebooks.js`, `blogActivity.js`, and `magic-urls.js` to use the helper. 2. Remove stale zero-byte `public/data/*.db`. 3. Document which files in `data/` are source snapshots and which are derived artifacts. 4. Make `git.js` fail on core sync errors instead of catching and continuing. ### 3. Canonical URL behavior is duplicated and drifting Evidence: - `next.config.mjs` builds sexy URL rewrites directly from `content.db`. - `src/lib/canonical-url.ts` mirrors the type list and reserved slug rules. - `src/app/(content)/[type]/[category]/[slug]/page.tsx` uses `/${slug}` for metadata but hierarchical URLs for JSON-LD and breadcrumbs. - Shared components fall back to `/${slug}` in `src/components/content/table.tsx`, `src/components/content/directory.tsx`, and `src/components/content/mediaCard.tsx`. - Some clients pass `route` fields that are ignored by shared components. Why it matters: The site convention is sexy URLs. Today that rule is implemented partly by rewrite generation, partly by helper functions, and partly by UI component fallbacks. SEO, JSON-LD, sitemap, breadcrumbs, and client links can disagree. Fix: Normalize URLs server-side and pass `item.url` everywhere. Components should render one URL field instead of rebuilding URLs locally. First batch: 1. Add a server helper that maps DB rows to normalized content cards with `url`. 2. Use `getCanonicalContentUrl` or a generated route manifest in metadata, JSON-LD, breadcrumbs, citations, and view trackers. 3. Remove fallback URL building from table, directory, and media-card components after callers pass URLs. ## Severity 2: App Structure and Route Ownership ### 4. `src/app` contains content payloads Evidence: - `src/app/(content)/til/content/*.mdx` - `src/app/(content)/now/content/*.mdx` - `src/app/(misc)/surveys/content/*.survey.md` Why it matters: The App Router tree should show routing and rendering ownership. Source content inside route folders makes it harder to scan the app tree and blurs the line between code and authored content. Fix: Move these payloads to `src/content/...` or the external content repo, then keep `src/app` limited to route modules, layouts, loading states, route-local clients, and route-local styles. First batch: 1. Move TIL and Now content loaders behind `src/lib/til.ts` and `src/lib/now.ts`. 2. Move the physical files only after imports are centralized. 3. Repeat for surveys after the route behavior is covered by tests. ### 5. TIL and Now detail pages load too much Evidence: - `src/app/(content)/til/page.tsx` - `src/app/(content)/til/[slug]/page.tsx` - `src/app/(content)/now/page.tsx` - `src/app/(content)/now/[slug]/page.tsx` Why it matters: Index and detail pages repeat full-collection loading and MDX import logic. Detail routes should not scale with collection size. Fix: Extract loaders: - `getTilEntries()` - `getTilEntry(slug)` - `getNowEntries()` - `getNowEntry(slug)` Then use collection loaders only for indexes and static params. ### 6. Route groups have dead or unclear boundaries Evidence: - `src/app/(info)` is empty. - `src/app/(media)` is empty. - Media-like pages live under `(tracking)`. - Misc pages under `(misc)` include several unrelated page families. Why it matters: Empty and vague groups weaken the mental map. The App Router tree should answer "where does this page belong?" quickly. Fix: Remove empty route groups or add a short route ownership note in `.config/.codex/docs/routes.md` and the architecture map. ## Severity 2: Client Boundaries and Repeated UI State ### 7. Layout header is client-side too high in the tree Evidence: - `src/components/layout/header.tsx` starts with `"use client"`. - It is imported broadly by server-rendered pages. Why it matters: Mostly static header markup should not widen the client boundary. The interactive part should be a small island. Fix: Split into: - server-safe header renderer - small client help or tooltip component - pure formatting helpers ### 8. Listing clients repeat the same state machinery Evidence: - `src/app/(content)/[type]/client.tsx` - `src/app/(content)/til/client.tsx` - `src/app/(content)/now/client.tsx` - `src/app/(content)/scripts/client.tsx` - `src/app/(content)/sequences/client.tsx` Repeated logic includes: - search state - URL query view sync - category or tag option construction - date sorting - empty states - header data fallbacks - slug banners and single-entry modes Why it matters: Every new collection page copies decisions from older pages. Small behavior fixes then require many edits. Fix: Extract: - `src/hooks/useContentListingState.ts` - `src/components/content/CollectionShell.tsx` - `src/components/content/FacetIndex.tsx` Keep route clients responsible only for data shape and item renderer choice. ### 9. Tracking pages repeat browser scaffolding Evidence: - `src/components/media/tracking-overview.tsx` already contains a shared renderer. - `src/app/(tracking)/games/client.tsx` still hand-rolls tabs, stat strips, sections, pagination, and empty states. - `src/app/(tracking)/film/watched/client.tsx` and `src/app/(tracking)/anime/watched/client.tsx` repeat filter and grid scaffolding. Fix: Extend `TrackingOverview`, then add a generic `TrackingGridBrowser` for watched/read/played lists. ### 10. `PageDescription` owns too many jobs Evidence: - `src/components/layout/page-description.tsx` Current responsibilities: - markdown-link parsing - icon lookup - modal open state - sounds - compact modal markup - expanded modal markup Fix: Extract: - pure `parseMarkdownLinks` helper with tests - shared modal body component - narrow client shell for open and close state ## Severity 2: CSS and Theme Structure ### 11. `globals.css` is a catch-all Evidence: - `src/app/globals.css` is about 1,979 lines. - It contains imports, viewport behavior, code block overrides, hover-card primitives, typography, footer animation, comments, sidenotes, footnotes, lede cards, and print styles. Why it matters: Global CSS is now a shared junk drawer. It is hard to know whether a class is global by design, legacy, or route-specific. Fix: Move domain styles into `src/app/styles/core/*` and import them through a single file: - `base.css` - `typography.css` - `code.css` - `comments.css` - `footnotes.css` - `sidenotes.css` - `print.css` - `cards.css` - `motion.css` Keep `globals.css` as the import root plus true app-wide tokens. ### 12. Theme tokens duplicate fallback and class-based modes Evidence: - `src/app/styles/themes/default.css` - duplicate no-JS dark-mode and `.dark` definitions - repeated pastel token blocks Fix: Use layered token groups or generate theme CSS from one source. The first target is no behavior change: reduce duplicate declarations while preserving fallback behavior. ## Severity 3: Scripts and Tooling Shape ### 13. Private operational scripts live under `public/` Evidence: - `public/scripts/dev/*` - `public/scripts/doc/*` - `public/scripts/prose/*` - `public/scripts/verse/*` - `public/scripts/doc/filelist.txt` Why it matters: Only browser-delivered scripts belong under `public/`. Commit tooling, DB mutators, importers, document sync, and prose tools should not be web-served. Fix: Target shape: - `public/scripts/footnotes.js` - `public/scripts/sidenotes.js` - `public/scripts/prod/*` only if browser-delivered or server-used from public path - `scripts/dev/*` - `scripts/doc/*` - `scripts/prose/*` - `scripts/verse/*` - `scripts/lib/*` Migration needs compatibility shims because project memory currently says script subdirectories are fixed under `public/scripts/{auth,dev,doc,prod,prose,verse}`. This should be changed deliberately in the architecture docs when the migration starts. ### 14. Script comments and hardcoded paths are stale Evidence: - `public/scripts/dev/syncContent.js` still documents `public/scripts/keep`. - `public/scripts/dev/generateMetadata.js` hardcodes `PROJECT_ROOT = '/home/krisyotam/dev/krisyotam.com'`. - Several scripts hardcode DB or content locations instead of deriving from project root and env vars. Fix: After the path helper lands, convert scripts one family at a time and remove stale comments while touching each file. ## Severity 3: Large Data Modules and Testing ### 15. Database modules are too broad Evidence: - `src/lib/media-db.ts` is about 837 lines. - `src/lib/data.ts` is about 713 lines. - `src/lib/system-db.ts` is about 620 lines. - Direct SQL is spread across app routes, lib modules, and scripts. Fix: Split by domain once the registry exists: - content collections - content taxonomy - content detail lookup - media film - media tv - media anime and manga - media games - system roll pages - system social pages Avoid splitting before the registry and DB path fixes, or it will preserve the same drift in more files. ### 16. Tests cover utilities more than structure Evidence: - `vitest.config.ts` uses Node environment only. - Existing tests focus on pure utility and config behavior. - Route clients, route handlers, URL normalization, DB path resolution, and theme tokens have little visible coverage. Fix: Start with tests that protect the refactors: 1. Content registry consumers agree on valid types. 2. DB path helper resolves root `data/` by default. 3. Canonical URL resolver returns sexy URLs and document overrides. 4. Listing state hook filters and sorts deterministically. 5. API route handlers return consistent error shapes. Add jsdom only for components that actually need browser interaction. ## Proposed Work Division After Deduplication This is the cleanest split between Claude and Codex without both agents touching the same files at once. ### Codex-owned first batch Files likely touched: - `src/lib/db.ts` - new `scripts/lib/paths.mjs` - `public/scripts/doc/syncDocs.js` - `public/scripts/dev/syncNotebooks.js` - `public/scripts/dev/blogActivity.js` - `public/scripts/prod/magic-urls.js` - `public/scripts/dev/git.js` - tests for DB path behavior Goal: Fix DB root drift and make script path resolution trustworthy. ### Claude-owned first batch Files likely touched: - `src/app/(content)/[type]/config.ts` - `src/lib/seo.ts` - `src/lib/content.ts` - `src/app/sitemap.ts` - `src/app/api/content/search/route.ts` - new content registry file - tests for registry consistency Goal: Create the content registry and convert read-only consumers. ### Codex-owned second batch Files likely touched: - `src/lib/canonical-url.ts` - `src/app/(content)/[type]/[category]/[slug]/page.tsx` - `src/app/(content)/[type]/page.tsx` - `src/components/content/table.tsx` - `src/components/content/directory.tsx` - `src/components/content/mediaCard.tsx` Goal: Normalize canonical URLs once and pass `item.url` through shared components. ### Claude-owned second batch Files likely touched: - `src/app/(content)/til/page.tsx` - `src/app/(content)/til/[slug]/page.tsx` - `src/app/(content)/now/page.tsx` - `src/app/(content)/now/[slug]/page.tsx` - new `src/lib/til.ts` - new `src/lib/now.ts` Goal: Extract TIL and Now loaders before moving content out of route folders. ### Later shared batches - CSS split: one agent owns `globals.css` extraction; the other only reviews imports. - Listing shell: one agent owns content listing state; the other owns tracking grid browser. - Script migration out of `public/`: do only after path helper and docs are updated. ## Recommended Order 1. Fix DB root drift and path helper. 2. Add content registry and convert read-only consumers. 3. Normalize canonical URL generation and require `item.url`. 4. Extract TIL and Now loaders. 5. Split header and `PageDescription` client islands. 6. Extract listing and tracking primitives. 7. Split `globals.css`. 8. Move private scripts out of `public/`. 9. Split broad DB modules by domain. ## Notes for Merge With Claude Audit No `claudeAudit.md` was present in the repo root when this file was written. When Claude's file appears, merge by theme rather than by agent: - Data and scripts - Content registry - Canonical URLs - Route tree ownership - Client boundaries - CSS and themes - Tests - Work division Deduplicate by file path before assigning fixes. The files most likely to attract conflicts are: - `src/app/(content)/[type]/config.ts` - `src/lib/seo.ts` - `src/lib/content.ts` - `src/lib/data.ts` - `src/lib/canonical-url.ts` - `next.config.mjs` - `src/app/globals.css` - `public/scripts/dev/git.js` - `public/scripts/dev/syncContent.js`