πŸ”¬ Codebase analysis

The Analysis Report

The saved, source-of-truth analysis every page of this course was written from. Analyzed 2026-07-14 at commit c32119a β€” 41 commits, ~12,000 lines of TypeScript.

TL;DR

Wera is a personal-but-SaaS-shaped internship application OS: Next.js app + five packages, Postgres via Drizzle, Clerk auth behind a seam, Trigger.dev background jobs, a sandboxed LaTeX→PDF pipeline, and an AI boundary that validates everything and logs every cent. It was built spec-first, riskiest-link-first.

What it is

A hosted web app for one CS student (architected for many) that runs the volume-with-quality loop: discover fresh internship postings fast, tailor a resume grounded in real profile evidence, apply ASAP, and see momentum on a scoreboard. The core object is an application, not a document; tracking exists to motivate volume, not to be a CRM.

πŸ–₯️
apps/web β€” the app

Next.js server components read from stores; ~30 server actions write. No REST layer at all.

🧠
packages/core β€” pure domain

Status state machine, needs-flags math, evidence resolution. Zero I/O, 100% unit-testable.

πŸ—„οΈ
packages/db β€” the stores

Drizzle schema (23 tables) + four store factories. Every query scoped by user_id.

πŸ€–
packages/ai β€” the boundary

Budget guard β†’ prompt β†’ zod validation β†’ retry once β†’ typed error. Every call logged to ai_runs.

πŸ”Œ
packages/connectors β€” the adapters

Six job sources behind one typed interface; unparseable items quarantined with raw JSON kept.

πŸ“„
packages/resume β€” the compiler

Tectonic subprocess with shell-escape blocklist, secretless env, hard timeout, hermetic package cache.

Tech stack & why

TechnologyRoleWhy it won
Next.js 15 + React 19App, server components, server actionsHosted from day one; server actions replace an entire REST layer
PostgreSQL (Neon) + DrizzleData + ORMManaged Postgres over an HTTP driver β€” no connection pool on serverless; schema as TypeScript source of truth
ClerkAuthFastest hosted auth, deliberately isolated behind getCurrentUser() so migration stays possible
Trigger.dev v4Background jobsCompile, discovery cron, reminders live outside serverless request lifecycles; custom build extension bakes the LaTeX toolchain
Tectonic 0.16.9 (pinned)LaTeX→PDFSelf-contained engine with --untrusted and --only-cached — sandboxable and hermetic
Cloudflare R2PDF storageS3-compatible, cheap egress, keys namespaced {userId}/…
OpenAI (behind a seam)Production AIA deterministic heuristic client serves dev/test; the interface hides which one is running
ZodValidation everywhereConnector inputs, raw items, AI outputs, task payloads β€” nothing unvalidated crosses a boundary
Vitest + Playwright + PGliteTestsPGlite gives a real in-process Postgres, so integration tests need no Docker or network

The monorepo, annotated

apps/web/ the Next.js app β€” pages, server actions, app-side seams, Trigger tasks
src/app/actions.ts~30 server actions, 1,746 lines β€” every write in the product
src/lib/auth.tsClerk β†’ internal user sync (the auth seam)
src/lib/ai.tsheuristic + OpenAI raw clients, provider wiring
src/lib/safe-fetch.tsthe SSRF guard for user-pasted URLs
src/trigger/compile-resume, scheduled discovery, reminder delivery
packages/core/ pure domain logic β€” state machine, needs-flags, evidence refs (166 lines, no imports from anywhere)
packages/db/ schema.ts (23 tables), 7 migrations, 4 store factories, PGlite test harness
packages/ai/ AiProvider boundary + domain AI functions + their zod schemas
packages/connectors/ SourceConnector interface, fingerprint recipe, 6 connectors (1,151 lines)
packages/resume/ Tectonic compile pipeline, LaTeX escaping, R2 upload
docs/ + DESIGN.md + plans/ the reviewed spec, implementation handoff, design system, advisor plans β€” the paper trail

The build order (reconstructed from git)

1
Spec & adversarial review before code

The product spec went through engineering, design, and cross-model review β€” findings folded back in β€” before scaffolding.

2
M1 β€” Scaffold + Tectonic compile spike

The least-proven link (LaTeX→PDF in the target runtime) proven first, with hermetic caching and escaping.

3
M2 β€” Schema + auth foundations

23 tables and the Clerk seam. Everything downstream needs user_id to exist.

4
M3/M4 β€” The loop, thin

Manual import β†’ AI analysis β†’ evidence-gated tailoring β†’ PDF β†’ mark applied β†’ scoreboard. The product was usable here.

5
Review-hardening interlude

Five advisor plans as parallel branches: CSV helpers + tests, ingest caps, fetch timeouts, SSRF guard, PGlite harness.

6
M5 β€” Fresh discovery

Connector interface, Greenhouse/Lever/public lists, scheduled runs, dedup + posting lifecycle, then Apify/Instagram bolt-ons.

7
M6 β€” Workflow surfaces

CRM detail, tasks and reminders with hourly delivery, communication drafts, the approval gate, weekly planning.

8
Design-system passes

Audit findings landed as style(design) commits; the "instrument-alive" motion system came last, driven by user feedback.

Patterns the codebase teaches

πŸ”

Idempotency keys everywhere

Deterministic keys + unique indexes turn retries from hazards into no-ops: sourceRun:scheduled:{key}:{day}, system:{app}:{type}.

🎫

Errors as values

AiResult, CompileResult, SourceRunResult are discriminated unions. throw is reserved for programmer errors.

🏭

Store factories + injected seams

createApplicationStore(db = createDb()). Process runner, AI client, fetch, clock β€” all injectable, all testable offline.

🧫

Quarantine over rejection

Parse failures become stored artifacts with raw payloads β€” schema drift is a visible health metric, not a 6 a.m. exception.

🧾

Receipts for AI claims

Every generated bullet carries evidenceRefs that must resolve to real profile items β€” hallucination is a type error.

πŸ—οΈ

Deploy-time proofs

The image build re-compiles with --only-cached to prove the LaTeX cache is complete β€” a broken cache fails the deploy, not a user.

Real bugs preserved in the history

BugRoot causeWhere it's taught
Saved-source provider poisoningRun ingestion upserted the connector row and overwrote the saved provider with an internal string β€” the source vanished from scheduled discoveryModule 5
CSV historical rows counted as applied-todayappliedAt omitted vs explicitly null mean different things; naive defaulting faked datesModule 6
Template not found in deploy bundleRepo-relative paths don't exist inside the deployed task image (open TODO)Module 4
Serverless timeout on big listsUnbounded public-list ingestion; fixed with manual/scheduled capsModule 5
Week boundary miscountsLocal-timezone getDay() vs UTC-stored appliedAt; fixed with UTC week mathModule 6
One bad timestamp aborts a querySQL ::timestamptz cast fails the whole statement; fixed by parsing per-row in JSModule 5

The portable markdown version of this report lives at analysis/codebase-analysis.md in the course folder β€” same content, plus verified primary-source links and per-component deep technical notes.