Executive summary
This portfolio architects a $0-cost platform against one business problem: employment — built end-to-end, hardware through distributed systems, entirely inside free-tier limits.
Jump to Mission & $0 costOutbound is résumé-first; inbound is LinkedIn-first. Both land here, and the site is tailored to how recruiters, hiring managers, and the technical team each actually screen.
Jump to The funnel & stakeholdersThe goal is synchronous, peer-to-peer signal over asynchronous prescreens — assessing collegiality and communication horizontally and vertically. Interviewing is a two-way evaluation, not a one-way test.
Jump to The funnel & stakeholdersThe SWE Compass, the agent definition, system-design philosophy, business-driven development, unintended consequences, and effective communication are first-principles lessons from years across domains — tools for scoping ambiguous problems, not dogma.
Jump to The SWE CompassEvery decision traces back to revenue, risk, time, or process — not novelty or the wrong KPI. A system that scales to 1M users is pointless if the grassroots users aren't there yet.
Jump to System design philosophyChoosing a translation's target dialect or launch market isn't a neutral engineering call — a Kyoto-vs-Tokyo default or a Brazil-only Portuguese launch carries bias, geopolitical, and PR weight no system diagram captures.
Jump to Unintended consequencesChronological for a lay audience, reverse-chronological — demo first — for a technical one, and shared experience to build real rapport. All three calibrated to who's actually in the room.
Jump to Effective communicationThere's too much to know to memorize. The six models above only work combined, live, and applied to whatever problem is actually in front of you.
Jump to How it all combinesOn this page
Why this exists, and why it costs $0
This portfolio's mission is "show, don't tell": instead of listing AI/ML engineering, data engineering, and agentic-methods skills on a résumé, it runs them, live, in production. A recruiter reading a bullet point about "agentic AI" has to take it on faith; a recruiter watching a real chat agent cite live Postgres numbers, a real GPU inference backend answer a request on Hugging Face's free ZeroGPU tier, or a real tailoring pipeline attach a PDF to an application is watching engineering happen.
The constraint that makes that proof credible is that none of it costs anything to run, indefinitely. That's not a cost-cutting afterthought — it's the actual design requirement this whole platform is built against. Every layer runs on a free tier: GitHub is the data warehouse and logic engine (a GitHub Actions cron drives ingestion through a raw → staging → production lifecycle, with unlimited compute minutes on a public repo), Vercel is a thin presentation layer (Next.js 16, rendering pre-processed data rather than doing heavy compute), and Hugging Face is the inference engine (ZeroGPU today, Colab's free GPU/TPU tiers next). Staying inside those limits — Vercel's rolling 100-deploys/24h window, GitHub's once-a-day free cron cap, ZeroGPU's per-call time quota — is itself the engineering exercise, not a constraint worked around once and forgotten.
That's also why this project treats AI/ML engineering and data engineering as one discipline rather than two: the Job Board's Neon Postgres pipeline, the résumé-tailoring agent, and this site's own RAG chat agent all move through the same shape — ingest real data, reason over it under a bounded compute budget, and serve the result — whether the compute is a GitHub Action, a Vercel function, or a Hugging Face Space. Agentic methods are the mechanism that makes that shape work end-to-end without a human in the loop for every step.
The pipeline this site is the landing page for
Two channels feed the same funnel, and this site is the landing page for both.
Outbound is résumé-first: the apply-to-jobs pipeline (see the Job Board section below) scrapes real postings, tailors a résumé per role through a bounded-concurrency Gemini pipeline, checks every draft against a golden-dataset QA rubric, and renders it to a PDF that gets attached directly to the application. That PDF is the artifact a recruiter or an ATS sees first — this site is where they land next, to verify the claims on it.
Inbound is LinkedIn-first, and slower by design: the roadmap's content phase turns the same engineering arc — design decisions, trade-offs, lessons learned — into a YouTube and LinkedIn content stream, so the portfolio is discoverable by people who were never sent a résumé at all.
Both channels converge on the same three stakeholders in a recruiting pipeline, and each reads this site differently:
- Recruiters scan for role fit, availability, and work authorization fast — the résumé and the About Me section are built for that pass.
- Hiring managerswant outcomes and judgment — the pipeline board, the roadmap phases, and this changelog's lessons-learned are aimed at that read.
- The technical team wants depth — architecture diagrams, real trade-offs, and, when they ask, a chat agent that can go as deep as the résumé itself allows without fabricating a number.
The chat agent embedded on this site is built for exactly that range: instructed to answer "professionally, accurately, and persuasively" and to recommend contacting Thomas after every response, but also to reach for concrete tech-stack and engineering-depth detail the moment a technical question shows up — one agent has to satisfy a non-technical recruiter's first pass and a technical interviewer's follow-up, without switching tools.
Flagged: this section is newly-authored synthesis of real, separately-documented facts — not a verbatim quote from any single existing file.
The SWE Compass
Before the details — design, data, model, backend, frontend, ops; hardware up through distributed systems; the version history below — there's one mental model that organizes all of it, and it's worth drawing before explaining.
The compass has three axes from one origin, not two — it's not a 2D chart with an x and a y. End-to-end points right: the lifecycle a system moves through from design to data to model to backend to frontend to ops. Abstraction points up: the ladder from raw hardware up through distributed systems. Time points down-left: iteration — what shipped, what broke, what got learned, in that order, every release.
End-to-end — design → data → model → backend → frontend → ops. This is deliberately close to, but not the same vocabulary as, the five-tier client → frontend → backend → model → data model that powers the System Design carousel on the homepage (src/constants/systemDesign.ts) — that model is a rendering taxonomy for diagramming any one project's architecture; this axis is the broader engineering lifecyclea system moves through. It's concrete enough that it's literally how the Study Plan whiteboard is organized: /study-plan has one board each named design, data, model, backend, frontend, and ops — this axis isn't an abstract claim, it's the same six words as six real, working boards.
Abstraction — hardware → distributed systems. This is the ladder from a single CUDA kernel up to a fleet of them coordinating. It's the Study Plan's GPU-TPU, cuda-flash-attention, cuda-distributed-computing, and system_design_MLEboards on the low end, and the ZeroGPU backend's runtime-JIT CUDA kernel benchmark and paged-attention/paged-state serving notes (documented on the Hugging Face page) on the high end — the same climb, from a kernel running on one GPU slice to a serving layer built to run against many.
That climb keeps going past the two rungs above: the CUDA kernel is compiled by ZeroGPU's own runtime-JIT step, which in turn is driven by a programming language — Python for the Hugging Face ML backend, TypeScript for this site — and each language sits inside a framework one rung up, TensorFlow/scikit-learn on the backend and Next.js here. Above any single framework is open source itself: agentic-writer, linkedin-content-loop, and resumeare this project's own open-sourced repos, sitting in the same ecosystem Next.jsitself comes from. Above that is infrastructure this project doesn't run at all — a native cloud provider like AWS DynamoDBfor the roadmap's NoSQL store, and third-party cloud/SaaS like Vercel, Neon, and Vercel Blob for everything else this site is hosted and stored on. One rung higher still is agents-as-a-service — the Gemini API, a hosted generative model consumed over an API rather than run locally. The top of the ladder is where this closes the loop back to "a fleet of them coordinating": GitHub Actions, Vercel, and Hugging Face acting as one "GitHub Monolith" is that same distributed-systems rung, three separate providers running as a single system.
Every real system carries a time axis independent of any one page: the legacy decisions already made and now load-bearing, whether anyone remembers making them or not; the actual scale it runs at today, not the scale it was originally designed for; and the future scale it has to survive to without a rewrite, since a rewrite is itself a legacy decision the next version inherits. This page is one concrete instance of that general shape, not an exception to it.
Time — iteration, and this page. This axis isthe changelog below: not a marketing timeline, but a dated, milestone-level record — reconstructed from real commits — of what shipped, what broke, and what was learned each release, per project. The 402 paid-tier-gate bug in the ZeroGPU deploy, the Postgres schema-drift bug that crashed the Job Board's live query, the CSS theme-cascade bug that shipped unreadable text to production — that's what this axis looks like in practice, not an abstraction about "iterating fast."
Read together: build the full lifecycle (end-to-end), understand it from the hardware up (abstraction), and expect to revisit both as you learn (time) — the three axes this site is organized around, literally, in the sidebar to the left.
The end-to-end axis above — design → data → model → backend → frontend → ops — isn't an invented taxonomy. It's this portfolio's own concrete instance of a more general pattern that recurs, under different names, in every named engineering lifecycle: the Software Development Lifecycle, the data engineering lifecycle, the ML/MLOps lifecycle, and the DevOps lifecycle. Each defines, builds, validates, ships, and operates in its own vocabulary; strip that vocabulary away and the same shape is underneath all of them. The full background on each of those 4 lifecycles, plus the abstraction that unifies them, lives at the Development Lifecycles section of /system-design — this page doesn't repeat that depth, it names the parallel.
"Navigate ambiguity by understanding what to use, when to use it, and why to use it."
— Thomas To
What "agent" means on this site
"Agent" gets used loosely enough industry-wide that it's worth defining by mechanism instead of adjective. On this site, something only gets called an agent if it does all four of these, observably:
- It perceives real data.Not a hardcoded fixture — the apply-to-jobs pipeline scrapes live Apple and NVIDIA postings; this site's chat agent reads the actual résumé markdown and live Postgres pipeline counts at request time.
- It reasons under a bounded constraint, through an LLM. apply-to-jobs tailors a résumé per role through a bounded-concurrency Gemini pipeline with quota-vs-rate-limit-aware backoff — deciding, per call, whether a failure means "back off" or "this model's daily quota is actually exhausted, fail over." The chat agent reasons over that same résumé under a fixed system prompt and a token budget, not an open-ended one.
- It acts — it produces a real, shippable artifact. apply-to-jobs's output is a rendered, uploaded PDF attached to a real application, not a suggestion a human has to rewrite. linkedin-content-loop and agentic-writer — both extracted into standalone open-source repos out of this same lineage — close a similar loop for content generation and LinkedIn posting automation.
- It gets checked before it ships.Every tailored résumé is auto-checked post-generation against a golden-dataset rubric — weak or missing quantifiable metrics, unsupported skill claims, missing hyperlinks, banned words, semicolons, a hard 2-page limit — before it's ever attached to an application. An agent that isn't checked before it acts isn't trusted enough to call one.
By that bar, this site's chat widget is a smalleragent than apply-to-jobs — it perceives and reasons, but its "action" is a streamed answer, not a shipped artifact — and that's fine; the definition is about mechanism, not about which one is more impressive.
Note the correction from the design pass: point 3 no longer asserts a specific internal "draft → review → validate → publish → archive" pipeline for agentic-writer/linkedin-content-loop — that detail isn't confirmed anywhere this repo can see. Point 4's rubric list restores "semicolons" to match GITHUB_CONTEXT §7 exactly.
Prompt engineering is the actual mechanism behind point 2 — how the reasoning step above gets steered, not just that it happens. The default template used across this project's agents is deliberately simple: state the objective, the steps to complete it, at least one explicit constraint, and a known example where one exists — with the balance shifting toward more constraints as a task gets more consequential (a résumé rubric check is constraint-heavy; a chat agent's tone guidance is not).
Zero-shot prompting — intentional ambiguity, no worked example — is the right call when the goal is agentic creativity and flexibility: letting a model find its own path through an underspecified problem. Few-shot prompting, and more structured strategies like chain-of-thought or tree-of-thought, are the right call for deterministic, workflow-shaped tasks, where the intent is a specific, repeatable outcome, not a flexible one. Choosing between them is itself part of the design: apply-to-jobs' résumé-tailoring pipeline needs a deterministic, rubric-checked output, so it leans on few-shot examples and explicit constraints; this site's chat agent has to handle an open set of recruiter questions, so its prompt leaves more room for zero-shot reasoning inside a bounded system prompt. See Google Cloud's prompt-engineering guide for the underlying strategies.
Reliability, maintainability, adaptability, resilience — not just CAP theorem, and not just for its own sake
CAP theorem is the textbook reflex the moment "distributed systems" comes up: pick two of consistency, availability, and partition tolerance. It's not wrong, but it's the wrong first question for a system whose binding constraint isn't a distributed-consensus trade-off — it's a $0 budget. That budget rules out an entire axis CAP doesn't talk about: this system cannot vertically scale its way out of a problem. There's no bigger Vercel plan, no bigger compute instance, no upgrade path that costs money, by design. So the axes that actually drive design decisions here are reliability, maintainability, adaptability, and resilience — and the concrete recommendation that falls out of that constraint is to scale horizontally, by partitioning work across several free-tier providers, rather than vertically within any one of them.
That four-axis list isn't invented from scratch — it's a deliberate adaptation of Chip Huyen's canonical four characteristics of a well-designed system: reliability, scalability, maintainability, and adaptability, from Designing Machine Learning Systems (O'Reilly, 2022). Three of her four carry over here unchanged: reliability, maintainability, adaptability. The fourth — scalability — is deliberately swapped for resilience, and not as a hedge: a system that structurally cannot vertically scale (the $0-budget constraint named above) doesn't get to claim scalability as a real, honest axis of its own design. What it can still be judged on is whether it survives a provider outage or a policy change without going down, which is what resilience measures here. Textbook version: reliability, scalability, maintainability, adaptability. Applied version: reliability, maintainability, adaptability, resilience. The swap isn't an oversight — it's the constraint talking.
That's what the "GitHub Monolith" architecture actually is: GitHub does the ETL/compute (a public repo gets unlimited CI minutes and a full, multi-hour-runtime VM per job, not a serverless function's 10–60 second cap), Vercel does the thin presentation layer (built to receive pre-processed data, not to compute it), and Hugging Face does inference (offloaded so it never competes with the page's own budget). Each provider's free-tier limit resets on its own schedule — Vercel on a rolling 24-hour/30-day window, GitHub on a fixed monthly billing date — and the "Vercel-Pinger" pattern (a GitHub Action cron commits data, and that commit itself triggers a Vercel deploy) exists specifically to route around Vercel's free-tier cron cap without paying for a better one. The system-design docs calculate a concrete safe operating point from this: at 100 deploys/24h with a 20% buffer reserved for manual hotfixes, hourly automation (24 deploys/day) is the recommended safe maximum — going past roughly 15-minute intervals risks a lockout window that neither more money nor more cleverness gets you out of early. As the roadmap docs put it, this "Code Monolith" architecture hosted on GitHub exists to guarantee infrastructure resilience — the same codebase can redeploy to Hugging Face, Vercel, AWS, or GCP, so no single provider's outage or policy change is a single point of failure for the whole system.
The lessons-learned entries in this changelog are what reliability and resilience look like in practice here, not abstractions. The Job Board's schema-drift bug — a posted_datecolumn that changed type out from under a live read path — is a maintainability failure: two services (a writer and a reader) drifted out of shared understanding of a schema neither one fully owned. The ZeroGPU 402 paid-tier-gate bug is an adaptability failure: even a "free" tier hides paid-tier assumptions inside its own API surface, and the fix was adapting the deploy path around a constraint that wasn't documented anywhere. The CSS theme-cascade bug that shipped unreadable text to a production page that built cleanly locally is a reliability gap in verification, not in code — it only surfaced by checking the actual deployed page. None of these are CAP-theorem trade-offs; they're the four axes above, showing up as real incidents, at zero infrastructure cost, on a system built to survive them without a bigger budget to fall back on.
Business-driven, not academic.None of the axes above — reliability, maintainability, adaptability, resilience — are chosen because they sound better in a systems-design interview than "pick two of three." They're the axes that matter here because this project runs against a rubric borrowed from business, not research: does a design decision drive revenue, reduce risk, save time, or streamline a process? A vertically-scaled, paid Postgres tier doesn't do any of those four things better than the current $0 architecture does — it just spends money to be marginally more elegant, which is optimizing for the wrong thing. It's the same rubric that makes the schema-drift, 402-gate, and CSS-cascade bugs above worth writing up at all: each one cost real time to diagnose, and documenting the fix is what keeps the next incident from costing that same time twice. Technology chosen for its own sake — because it's new, or because a textbook covers it — is research. Technology chosen because it moves one of those four levers is engineering.
The same rubric applies one level up, before a system is even worth scaling: its users have to be scaled onto it first.
"You can design this amazing technology that can support 1M users, but from grassroots, if your users are afraid of the technology, then what's the point in designing a system that can scale to 1M users when you don't have that many users?"
— Thomas To
The lesson generalizes past any one system: scale is a hypothesis until real users adopt it, and grassroots trust is earned before headroom is spent building for a user count that may never show up. That's why this site treats adoption — a résumé PDF a recruiter actually opens, a chat agent a hiring manager actually trusts enough to ask a follow-up question — as the metric that comes before throughput, not after it.
Benchmark the existing process before automating it, not after. Any operational process improvement that replaces a manually-run workflow with an automated one starts by measuring the process as it already performs — cost, turnaround time, error rate — before a line of automation code exists, so the "after" number has a real "before" to be checked against. Skip that step and the risk isn't just a worse process; it's a process nobody can prove is better, which is the same trust gap the "AI Growth & Adoption" pull-quote above is about, one layer down.
Finance and time are the default success criteria — not the only ones. Cost saved and hours reclaimed map straight onto the business-driven-development rubric above and read clearly to a recruiter or hiring manager without translation. But quality or another qualitative measure is the honest criterion when the task calls for it — the tailored-résumé rubric check described under "What 'agent' means" is graded on whether the output passes, not on how fast it runs, because a faster résumé that fails the rubric is a regression wearing a KPI, not an improvement. Once a metric is picked, the discipline is in what doesn't get measured: instrumenting every available signal "just in case" produces test-fatigue nobody actually looks at, and it invites optimizing for a number the process was never scoped to move — scope creep, chasing a metric outside the original design's intent instead of the one that was actually the point.
The benchmark is also the gate on scale.A process proven against a narrow benchmark hasn't earned the right to run at ten times that scope just because the math says it could — the same grassroots-trust-before-headroom argument the pull-quote above makes about users, applied to the process itself. Rolling automation out past its established benchmark before addressing that hesitation — a team's or a stakeholder's fear of an automated process replacing one they trusted by hand — repeats the exact mistake the pull-quote warns against: capacity spent on scale nobody asked for yet, instead of on earning the trust scale depends on.
Designing for today's problem, not a guessed-at tomorrow. The harder half of applying reliability, maintainability, adaptability, and resilience isn't knowing the four axes exist — it's navigating the ambiguity of which one a given problem actually needs, right now, and building only that much of it. A system over-built for failure modes today's traffic will never hit, or scale today's budget will never reach, isn't more resilient than one sized to what's real — it's slower to ship and harder to reason about, which is its own reliability and maintainability cost. The version that adapts tomorrow is the one built honestly for what's true today, with room left to change, not the one pre-built for a future that was only ever a guess. None of this is really about the code; it's about architecting the system the code has to live inside.
Bias, geopolitics, and PR risk — not just system design
A systems-design interview at Apple raised this directly, through two related prompts in the same conversation. The first: design a system that translates English into another language. The obvious move is to pick a target language and start on the architecture — but the choice of target isn't a neutral technical default. "Chinese" isn't one language or one dialect; picking it without picking which variety, region, and political context it represents is itself a bias decision, made before a single API gets designed.
That gets sharper the moment the system does more than move text around. Text-to-text translation and text-to-voice translation aren't the same problem — voice has to carry dialect and regional accent, which flat text never surfaces. English → Japanese makes it concrete: Kyoto's dialect (Kansai-ben) and Tokyo's standard dialect are differing regional and contextual representations of the same language, not interchangeable defaults. A system that doesn't account for that risks a real mistranslation, not a cosmetic one — and shipping without accounting for that risk is a decision with consequences, not a neutral technical default someone else is responsible for.
The same interview's second prompt was two-way translation — English → Portuguese and Portuguese → English. The design question wasn't the algorithm; it was the launch market. Shipping that service targeted only at Brazil — one Portuguese-speaking market, not Portugal or the rest of the Lusophone world — looks like an ordinary "which market ships first" product call. It isn't just that: it carries geopolitical, historical, and public-relations weight for the company's reputation, far outside anything in the codebase.
The system design doesn't have to change — the engineer's awareness does. Generalized past translation specifically: an engineer can be asked to design a system — "build a translator" — and the system design itself may not need to change at all to account for any of this. What has to change is whether the engineer stays mindful of the real-world business, geopolitical, and reputational realities sitting around that design decision. Skip that awareness and the system still ships, still works, still passes review — and still produces unintended real-world consequences anyway. That awareness is a distinct discipline from the system design itself: business acumen and real-world judgment, layered on top of the technical design, not a substitute for it.
It's the same instinct as Not just what you say — how you say it — know who's actually in the room — taken to its most extreme, highest-stakes version. There, the room is one reader or one interviewer; here, the "room" is an entire user population, and the same audience-first calibration decides whether a design represents their dialect, region, or nationality faithfully, or doesn't.
It also compounds the exact risk the AI Growth & Adoption pull-quote raises about scaling to users who aren't there yet. A translation or localization choice that misrepresents or alienates a dialect, region, or nationality doesn't just ship a worse product — it erodes the grassroots trust adoption actually depends on. It's the same "users afraid of the technology" failure mode, except the fear here is earned by cultural or geopolitical insensitivity baked into a language or market decision, not unfamiliarity with the technology itself.
Flagged: this section is newly-authored synthesis distilling a real, specific interview experience — not a verbatim transcript or quote.
Not just what you say — how you say it
Every explanation on this site — this essay included — assumes a specific reader. Knowing who's actually in the room decides more than word choice: it decides how much can be assumed. A staff engineer doesn't need "GitHub Actions" defined; a non-technical hiring manager doesn't need it left undefined either — they need it translated into what it does ("a free, scheduled job runner") instead of what it's called. Reading the audience first, then picking the vocabulary, is the same discipline as reading a job description before tailoring a résumé to it — the content doesn't change, the framing does.
Chronological — background, scope, reasons, solution — is the default. For a lay or mixed audience, the order that lands is the order a story would use: what problem existed before this (background), how big or specific that problem actually was (scope), why the available options narrowed to this one, introduced only after the business problem is clear (reasons), and only then the built thing itself, backed by a live demonstration (solution). That's deliberately the same order this changelog uses project by project — problem, then what shipped, then what broke, then what was learned — because most readers arriving at any one entry haven't already internalized the context a technical peer would bring.
Reverse-chronological flips it for a technical room. Lead with the real, data-driven demonstration — a chat agent citing a live Postgres number, a résumé PDF that actually renders — and let the audience pull the background out with questions, instead of pushing it at them up front. A picture is a thousand words: a demonstration that actually works communicates the unspoken narrative — the debugging, the trade-offs, the constraint that shaped the design — without spelling any of it out first. A technical audience that watches something work will ask "how," and that question is a better entry point into the architecture than a slide ever is.
The same chronological/reverse-chronological split governs jargon and humor, not just structure. A lay audience gets plain language first and technical vocabulary only once a concept has already landed; a technical audience gets the term up front, because withholding it reads as padding, not clarity. Humor works the same way — save it for after a technical point lands with a technical room, open with it to lower the stakes for a non-technical one. Neither is more "correct" than the other; both are the same audience-first instinct applied to a different lever.
Every choice above — order, jargon, humor, which experience to share — is the same instinct pointed at a different lever: read the room first, then decide how to say the thing that's true anyway.
For a reusable version of this — stakeholder archetypes and a step-by-step framework for navigating any conversation — see Effective Communication.
"There's too much to know. From lessons learned in university, we apply mental models and derive solutions from first principles — applying what's needed to the problem we're actually trying to solve."
None of the six sections above are meant to be memorized as a checklist. In an actual conversation — working through a problem live, collaboratively, with an interviewer — they get combined on the fly: navigating horizontally, the way the SWE Compass' end-to-end lifecycle moves through design, data, model, backend, frontend, and ops, and vertically, up its abstraction ladder and across its time axis, while applying a business-driven-development rubric — and a mindfulness of the real-world consequences a design decision carries — to what's actually worth solving, and communicating that navigation out loud as it happens instead of presenting it after the fact. That combination — not any one model in isolation — is what this page is actually trying to demonstrate. It's not a rule to follow; it's a skill to leverage, differently, every time.
Job Board
Neon Postgres + résumé-tailoring pipeline
The first live, database-backed feature on this portfolio — real rows in a Neon Postgres table, synced by an external tailoring pipeline four times a day straight from each company's own job board, checking the database for staleness before every write. That staleness check is company-aware: NVIDIA rows are deleted once today passes their listed 'accepted at least until' date, while other companies get a longer, recruiter-informed 3–6 month window. Signed 24-hour résumé downloads, no ISR/revalidate window once filtering made per-request rendering cheap enough.
Open Job BoardCompany-aware staleness: NVIDIA's deadline replaces its missing posted date
The upstream ingestion pipeline that syncs the Job Board four times a day — scraping each company's own job board directly, checking the database for staleness before writing anything new — now treats 'stale' differently per company, and the Job Board itself surfaces the date it's actually keying off instead of a misleading 'Posted' label.
- Surfaced NVIDIA's 'accepting applications at least until' deadline on the Job Board, replacing posted_date as the effective date NVIDIA rows are labeled, bucketed, and sorted by — NVIDIA's own listings don't expose a real posted date, so a plain 'Posted <date>' line was actively misleading.
- Made the Job Board filter bar and pagination sticky as one unit so long, filtered result lists stay easy to re-filter without scrolling back up.
- Extended the staleness check the ingestion pipeline runs before every write to be company-aware: NVIDIA rows are deleted once today passes their listed 'accepted at least until' date, while other companies (e.g. Apple) are kept for a longer 3–6 month window, per recruiter feedback that a late applicant isn't automatically an unqualified one.
- NVIDIA is the one company in this pipeline that doesn't expose a real posted date, only an 'accepted at least until' deadline — so staleness couldn't mean the same thing for every row. The fix was one effective-date concept (COALESCE(applications_accepted_until, posted_date), NVIDIA-only) threaded through the ingestion pipeline's own delete check, the read query's bucketing/sort, and the UI label, not three separate special cases.
- Recruiter feedback that an interview loop can take a few months to circle back reshaped the staleness policy itself, not just the UI: postings without a hard deadline are kept for 3–6 months instead of NVIDIA's tighter cutoff, since a candidate applying late in that window is more likely earlier in a slow pipeline than actually unqualified.
- The private Vercel Blob résumé storage this feature is built on wasn't only an architecture exercise — it got used live, mid-interview, as a fast way to pull up and share a tailored résumé straight from the portfolio.
Job Board filtering, search, and pagination at scale
The Job Board grew real search, filtering, and pagination instead of a full-table fetch — which is what made removing the ISR/revalidate window safe under the zero-cost constraint.
- Added date-range and company filtering with pagination to the Job Board.
- Added search, a page-size selector, and in-place pagination to the Job Board.
- A full-table fetch doesn't stay free forever as a dataset grows — filtering and pagination moved the Job Board from one large query per page load to smaller, targeted queries, which is what made removing the old ISR/revalidate window (in favor of per-request rendering) safe under the zero-cost constraint.
Job Board goes live: real Postgres reads in production
The first live, database-backed feature on the portfolio — a Job Board reading real rows from a Neon Postgres table synced by an external tailoring pipeline.
- Added a Job Board fed by a Neon-backed API route.
- Documented the Job Board feature in the README and removed the stale Python ML backend it replaced.
- Wired the Jobs tab into the frontend as a tracker for cross-service database reads, and trimmed pipeline token usage on the writing side.
- Added signed, private Vercel Blob résumé downloads instead of public URLs.
- Debugged the posted_date column through TEXT → DATE → TEXT.
- Standardized posted dates to ISO 8601 (YYYY-MM-DD) formatting.
- Fixed a Job Board query crash caused by the live DATE-typed posted_date column.
- Switched résumé downloads to use the original filename instead of a generic jobId.
- Schema drift between a writer service and a reader service is a real failure mode, not a hypothetical one — the posted_date column changed type out from under the read path and crashed a live query before the fix.
- Private file delivery should never rely on a public URL — résumé PDFs moved to signed, time-boxed Vercel Blob links from day one of this feature.
[Add a real question an interviewer asked about the Job Board’s schema-drift bug]
[Add your real answer — e.g. how you diagnosed the posted_date TEXT→DATE→TEXT crash and what changed to prevent it recurring.]
[Add a real question about why Neon Postgres over another database option]
[Add your real answer here.]
[Add a real question about the résumé-download security model]
[Add your real answer — e.g. why signed, time-boxed Vercel Blob links instead of public URLs.]
Hugging Face / ZeroGPU Backend
GPU inference on Hugging Face's free tier
A real GPU inference backend on Hugging Face's free ZeroGPU tier — a runtime-JIT CUDA kernel benchmark and a paged-attention serving note, documented as a technical report rather than a marketing page, with the live Space embedded directly on /huggingface.
Open Hugging Face / ZeroGPU BackendZeroGPU as a technical report, plus a live Hugging Face Space page
Turned the backend status page into an actual technical report and gave the Hugging Face Space its own embedded page on the portfolio.
- Added a ZeroGPU constraints study and documented it directly in backend/app.py.
- Redesigned the ZeroGPU backend page as a light technical report.
- Added a Hugging Face Space page with an iframe embed and a compute-stack showcase.
- Added a paged-attention serving note and a pipeline diagram to the Hugging Face page.
- Documenting a platform's real constraints inside the code that hits them (backend/app.py) keeps the docs from drifting the way a separate wiki page would.
- "No screenshots, no static mockups" became a literal design constraint — an embedded live Space is a stronger proof-of-work signal than a description of one.
Standing up a real GPU inference backend on ZeroGPU
Replaced the placeholder backend page with an actual GPU inference service on Hugging Face's free ZeroGPU tier, then iterated hard on making the deploy and the page itself work.
- Added a GPU inference backend scaffold and deploy pipeline.
- Fixed the Hugging Face Space deploy by dropping create_repo, which was hitting a 402 on the paid-tier gate.
- Synced docs with the current architecture and made the ZeroGPU backend headless-only.
- Replaced the ZeroGPU placeholder with a runtime-JIT CUDA kernel benchmark.
- Redesigned the ZeroGPU backend page as a headless, datasheet-style status page.
- Fixed unreadable text on the deployed ZeroGPU backend page caused by a theme cascade bug.
- Even a "free" compute tier has paid-tier gates hiding inside its API surface — create_repo silently assumed paid-tier permissions and returned a 402 until it was dropped from the deploy path.
- A CSS theme cascade bug can ship an unreadable page to production even when the app builds cleanly — this one only surfaced by checking the actual deployed page, not local dev.
- A placeholder is not a benchmark — the page didn't earn a 'live technical' framing until the placeholder was replaced with a real runtime-JIT CUDA kernel benchmark.
[Add a real question about the 402 paid-tier-gate bug hit deploying to ZeroGPU]
[Add your real answer here.]
[Add a real question about why ZeroGPU instead of a dedicated GPU instance]
[Add your real answer here.]
[Add a real question about the CSS theme-cascade bug that shipped unreadable text to production]
[Add your real answer here.]
Study Plan (Excalidraw)
Discipline-specific live whiteboards
The live Excalidraw whiteboard at /study-plan, split into discipline-specific boards — design, data, model, backend, frontend, ops, plus the lower-level GPU/CUDA boards — instead of one unbounded canvas.
Open Study Plan (Excalidraw)Splitting the study whiteboard by discipline
The Excalidraw study-plan whiteboard grew a real information architecture: one board per engineering discipline instead of one large canvas.
- Added multi-board support to the Excalidraw study-plan whiteboard.
- Renamed technical_prep.excalidraw to SWE.excalidraw to match the new per-discipline naming.
- Split the study-plan whiteboard into discipline-specific boards.
- A single flat whiteboard file doesn't scale as content grows — splitting into named, discipline-specific boards made /study-plan legible again instead of one unbounded canvas.
[Add a real question about why one Excalidraw board was split into discipline-specific boards]
[Add your real answer here.]
[Add a real question about using Excalidraw as a thinking tool, not just a presentation one]
[Add your real answer here.]
[Add a real question about the fractional-indexing constraint on agentic writes to a board]
[Add your real answer here.]
Chat / RAG Agent
Résumé-grounded RAG, now with live pipeline KPIs
The résumé-grounded RAG chat widget embedded on the homepage. Its version history here is the thinnest of the four projects — which is itself informative: most of its evolution so far is this very page.
Changelog becomes a project-organized mental model
Restructured /changelog from one flat timeline into four per-project sections with their own version history and interview-FAQ scaffold, plus a full mission/stakeholders/SWE-Compass/agent-definition/system-design essay.
- Restructured /changelog into four per-project sections (Job Board, Hugging Face/ZeroGPU, Study Plan, Chat/RAG Agent), each with a sticky, scrollspy-driven sidebar.
- Added a full-page essay ($0 mission, résumé/LinkedIn funnel, SWE Compass, agent definition, system design philosophy) with an inline SVG recreation of the SWE Compass diagram.
- Added placeholder interview-FAQ slots per project, explicitly flagged isPlaceholder so nothing here is mistaken for real interview material.
- Organizing a changelog by project instead of strictly by date makes a thin-history project (the chat agent) visible on its own terms, instead of buried inside a mixed-purpose entry like v2.4.0.
- A mental-model essay this long only stays legible with real information architecture — a sticky sidebar and scrollspy reused from the homepage's own useActiveSection hook, not a new nav pattern invented for one page.
The chat agent gets live pipeline numbers
The chat agent's RAG context now cites live pipeline KPIs pulled straight from Postgres at request time, instead of a hand-copied number.
- Wired live pipeline KPIs into the chat agent's RAG context and updated the system design doc.
- A hand-copied metric goes stale the moment the underlying data changes — splicing live Postgres counts into the RAG system prompt at request time keeps the chat agent from citing a number that was only ever true at one point in time.
[Add a real question about how the RAG system prompt avoids hallucinating metrics]
[Add your real answer here.]
[Add a real question about splicing live Postgres KPIs into a static system prompt at request time]
[Add your real answer here.]
[Add a real question about the résumé-tailoring agent’s golden-dataset QA rubric]
[Add your real answer here.]
Open Source & Content Roadmap
Apple Lifestyle App
iOS Engineer / Health Data Architect
Fragmented biometric data across apps lacks vertical integration — no unified model connecting Apple Watch sensors to nutrition and activity planning.
Native iOS + WatchOS app using HealthKit to surface real-time HRV, VO2 max, and activity metrics into a unified lifestyle dashboard.
- Real-time biometric sync from Apple Watch
- Unified HRV + activity + nutrition view
- Vertical Apple ecosystem integration
Apple Lifestyle App
A native iOS + WatchOS app that unifies fragmented biometric data into one lifestyle dashboard, with on-device inference to keep sensitive health data private.
Apple Watch
HealthKit sensors
Captures HRV, VO2 max & activity in real time at the wrist.
iOS + WatchOS App
Swift · SwiftUI
Unified HRV + activity + nutrition dashboard across watch and phone.
HealthKit Pipeline
HealthKit · CloudKit sync
Normalizes biometric streams into one health graph.
Core ML
On-device inference
Local models keep biometric data private — no server round-trip.
CloudKit
iCloud private DB
Per-user encrypted store synced across the Apple ecosystem.
- Why on-device Core ML
- Health data is sensitive; on-device inference avoids ever sending biometrics off the phone.
- Trade-off
- Deep vertical integration (HealthKit/CloudKit) buys a seamless Apple experience at the cost of cross-platform portability.
YouTube: Bodyweight vs. Bodyfat Analysis
Data Science Communicator
The fitness community conflates 1% bodyweight loss with 1% bodyfat loss — these are different rate-limiting steps for macro nutrient allocation based on lean mass.
Data-driven video comparing both models, deriving macro targets from lean mass (not total weight), and visualizing the bottleneck difference using Python analysis.
- Visual proof of lean mass vs. total weight divergence
- Macro allocation model based on FFMI
- Accessible biochemistry for fitness audience
Bodyweight vs. Bodyfat Analysis
A data-driven analysis pipeline that separates 1% bodyweight loss from 1% bodyfat loss, deriving macro targets from lean mass rather than total weight.
YouTube Audience
Fitness community
Consumes the analysis as an explanatory video.
Python Analysis
Pandas · Matplotlib
Computes and visualizes the lean-mass vs total-weight divergence.
FFMI / Lean-Mass Model
Statistical model
Allocates macros from fat-free mass, exposing the true rate-limiter.
Body Composition Dataset
CSV · Pandas DataFrame
Measurements feeding the comparison.
- Why lean mass over total weight
- Macro allocation should track fat-free mass (FFMI); total-weight loss conflates fat and muscle and mis-sizes targets.
- Trade-off
- A communication/analysis pipeline, not a live service — no frontend or persistent backend by design.
Accelerated Compute at Scale
Systems / ML Infra Engineer
Scaling ML training across TPU/GPU fleets is bottlenecked by the compiler and orchestration layers most engineers treat as a black box — the low-level hardware-accelerated compute path.
Contribute upstream to OpenXLA, JAX, and Kubernetes while studying low-level hardware-accelerated compute, strengthening industry-standard library support such as PyTorch's C/C++ and Python bindings.
- Upstream contributions to OpenXLA, JAX & Kubernetes
- Fluency in low-level hardware-accelerated compute
- Industry-standard PyTorch C/C++ & Python support
Accelerated Compute at Scale
Upstream contributions to OpenXLA, JAX and Kubernetes, working the low-level hardware-accelerated compute path that most engineers treat as a black box.
Contributor
Upstream PRs
Engineer submitting to OpenXLA / JAX / Kubernetes.
Kubernetes
Fleet orchestration
Schedules training workloads across TPU/GPU nodes.
OpenXLA / JAX Compiler
XLA compilation
Lowers models to hardware-accelerated kernels.
TPU / GPU Training
PyTorch · JAX · C/C++
The low-level, hardware-accelerated compute path.
Sharded Datasets
Distributed I/O
Data pipelines feeding the accelerator fleet.
- Why go below the framework
- Scaling ML training is bottlenecked by the compiler and orchestration layers most engineers never open.
- Trade-off
- Upstream open-source work is high-leverage but slow — it favors depth in the low-level path over breadth.
Agentic Revenue Optimization
AI/ML Engineer
High biological variability in donor starting material (Leukopaks, Bone Marrow) led to unpredictable cell yields, causing inventory misalignment and lost revenue on rare cell types.
Architected a predictive model to classify donors by highest probable cell yield (optimizing for Rarity vs. Throughput). Deployed an Agentic Interface to bridge lab data with enterprise ERP systems, automating yield reporting for sales teams.
- Querying from hours to minutes
- Ability to select donors for orders
Agentic Revenue Optimization
A predictive model classifies donors by probable cell yield (rarity vs throughput); an agentic interface bridges lab data to SAP ERP, automating yield reporting for sales.
Sales & Ops
Enterprise users
Select donors by highest probable cell yield for orders.
Tableau BI
Dashboards · reports
Surfaces yield forecasts and automated reporting.
Agentic ERP Interface
Python · SAP · CRIO
Bridges lab data to SAP ERP, automating yield reporting.
Yield Classifier
Predictive model
Classifies donors by probable cell yield (rarity vs throughput).
Snowflake
Lab + CRM warehouse
CRIO/SAP data unified — querying in minutes, not hours.
- Why predict yield upfront
- High biological variability in donor material caused inventory misalignment and lost revenue on rare cell types.
- Trade-off
- Rarity vs throughput is a business trade-off — the model surfaces it for humans rather than deciding it.
Agentic Onboarding
AI/ML Engineer
Fragmented documentation and reliance on tribal knowledge (i.e word of mouth) caused slow onboarding and information silos.
RAG Agents fine-tuned to department specific standard operating procedures (SOP) for niche context with at least one (1) orchestrator agent with general context for cross-functional insight.
- Increased learning rate up to 80% (Wright's Law: Stanford-B model)
- Resource efficient contextual GenAI
Agentic Onboarding
Department-specific RAG agents fine-tuned to SOPs, coordinated by an orchestrator agent with general context for cross-functional insight — replacing tribal knowledge.
Employees
Chat interface
Ask department questions in natural language.
Chat UI
Conversational front door
A single entry point across departments.
Orchestrator + RAG Agents
MCP · Python · SQL
Dept-specific agents plus one orchestrator for cross-functional context.
Fine-tuned LLM (RAG)
SOP-grounded
Tuned to department SOPs for niche context.
Vector DB + Snowflake
Confluence · SAP embeddings
SOP and work-instruction embeddings for retrieval.
- Why RAG over fine-tuning alone
- Fragmented docs and word-of-mouth knowledge need retrieval grounding to stay current without constant retraining.
- Trade-off
- Resource-efficient contextual GenAI, but answer quality is bounded by how well SOPs are documented.
LinkedIn Content Loop
Automation Engineer
Manual LinkedIn post scheduling breaks consistency; no structured rotation system for pre-generated typed content.
CI/CD pipeline using GitHub Actions CRON to auto-rotate TypeScript-typed LinkedIn posts from a managed content queue.
- Fully automated post rotation
- TypeScript-typed content contracts
- Zero manual scheduling
LinkedIn Content Loop
A CI/CD pipeline that auto-rotates TypeScript-typed LinkedIn posts from a managed content queue on a GitHub Actions CRON schedule — zero manual scheduling.
LinkedIn Audience
Followers
Receives consistently scheduled posts.
GitHub Actions CRON
TypeScript · CI/CD
Auto-rotates posts from a managed queue on schedule.
Markdown Content Queue
TypeScript-typed contracts
Pre-generated posts with typed content contracts.
- Why GitHub Actions CRON
- Manual scheduling breaks consistency; a scheduled pipeline removes the context-switch entirely.
- Trade-off
- No model or UI by design — a deterministic rotation, not generative posting.
Resume & Cover Letter Tailor
AI/ML Engineer
Job applicants cannot efficiently customize resumes per job description while maintaining ATS compliance and professional quality.
Claude Sonnet sub-agents tailor a golden dataset (master resume) to specific JDs, generating ATS-compliant single-page PDFs via fpdf2.
- 79 banned words enforced
- Single-page PDF with zero manual intervention
- Batch processing across job descriptions
- 5-step cascading page-fit optimization
Resume & Cover Letter Tailor
Claude Sonnet sub-agents tailor a golden master résumé to specific job descriptions, generating ATS-compliant single-page PDFs via fpdf2 with zero manual intervention.
Applicant (CLI)
Batch invocation
Runs tailoring across many job descriptions.
Python + Docker
Python 3.12 · fpdf2
Orchestrates tailoring and renders single-page PDFs.
Claude Sonnet Sub-agents
ATS-compliant tailoring
Tailor the golden master résumé to each JD.
Golden Master Résumé
+ JD corpus
Single source dataset; 79 banned words enforced.
- Why a golden dataset
- One master résumé keeps quality and voice consistent while sub-agents specialize per job description.
- Trade-off
- 5-step cascading page-fit optimization guarantees single-page output at the cost of extra passes.
Agentic Writer
AI/ML Engineer
Technical professionals lack a structured system for creating platform-specific content with consistent quality and voice.
Claude Code plugin enforcing a content lifecycle (draft → review → validate → publish → archive) with 127 banned words and active voice validation.
- 127 banned words enforced
- 1,000–1,300 char LinkedIn posts
- 1,600–2,000 word Medium articles
- Active voice-only validation
Agentic Writer
A Claude Code plugin enforcing a content lifecycle (draft → review → validate → publish → archive) with banned-word and active-voice validation for a consistent house style.
Author (Claude Code)
Plugin invocation
Drives the content lifecycle from the CLI.
Claude Code Plugin
YAML · lifecycle engine
Enforces draft → review → validate → publish → archive.
Claude Sonnet
Prompt-engineered
Generates platform-specific content with active-voice validation.
Markdown + Rulesets
127 banned words
Typed content and validation lists.
- Why enforce a lifecycle
- A structured draft→publish→archive flow keeps voice and quality consistent across platforms.
- Trade-off
- Strict banned-word and active-voice gates reject more drafts but guarantee a house style.
Phase 2: Agentic Integration
Fullstack AI Engineer
Agentic projects exist only as local prototypes with no live demonstration path for interviews or portfolio visitors.
Implement proof-of-concept agentic features using Vercel serverless infrastructure, surfacing live demos on the portfolio homepage.
- Demonstrable agentic features live during interviews
- Serverless architecture on free-tier infrastructure
- Streaming chat with RAG context
Phase 2: Agentic Integration
Proof-of-concept agentic features on Vercel serverless infrastructure, surfacing live streaming demos with RAG context on the portfolio homepage.
Portfolio Visitor
Browser
Interacts with live agentic demos during interviews.
Next.js on Vercel
TypeScript · React
Serves demos on free-tier infrastructure.
Vercel Serverless
Streaming functions
Proof-of-concept agentic endpoints.
Google Gemini (RAG)
Streaming chat
RAG context grounds responses.
RAG Context
Résumé · JSON
Source documents injected at request time.
- Why serverless demos
- Live, demonstrable agentic features beat local prototypes for interviews — at zero hosting cost.
- Trade-off
- Serverless time limits cap demo complexity, so features are kept lightweight and streaming.
LinkedIn API CI/CD Pipeline
Content Automation Engineer
Manual LinkedIn posting breaks publishing consistency and requires context-switching during deep work.
Automate content publishing via LinkedIn API with OAuth 2.0 token refresh, triggered by GitHub Actions on a CRON schedule.
- Zero manual post intervention
- Consistent publishing cadence
- API-driven content rotation
LinkedIn API CI/CD Pipeline
Automated content publishing via the LinkedIn API with OAuth 2.0 token refresh, triggered by GitHub Actions on a CRON schedule — zero manual post intervention.
LinkedIn Audience
Followers
Receives API-published posts on cadence.
GitHub Actions CRON
TypeScript · CI/CD
Triggers publishing on schedule.
LinkedIn API (OAuth 2.0)
Token refresh
Publishes via API with automatic OAuth token refresh.
Content + Tokens
Rotation store
Queued content and refresh credentials.
- Why API over manual posting
- Manual posting breaks cadence and interrupts deep work; the API removes the human from the loop.
- Trade-off
- OAuth 2.0 token refresh adds moving parts versus manual posting, but eliminates ongoing intervention.
Apply-to-Jobs Pipeline
AI/ML Engineer
Manually scraping, tailoring, and tracking job postings does not scale — every role needs its own scraped description, tailored résumé, rendered PDF, and a way for a live job board to serve it.
A 5-stage TypeScript pipeline (scrape → describe → tailor → render → sync) scrapes Apple postings across 4 search filters, tailors a résumé per role through a 6-model Gemini fallback cascade, renders Markdown to PDF, and syncs into the same Neon Postgres table and private Vercel Blob store this portfolio's Job Board already reads from.
- 296/296 roles scraped (100%) across 4 Apple search filters — TPU, GPU, Infrastructure, Backend
- 179/296 résumés tailored (60.5%), the pipeline's bottleneck stage
- gemini-3.1-flash-lite carries 167/179 tailors (93.3%) at ~4.5s avg — the workhorse despite ranking #2 of 6
- Syncs into the same Neon "PORTFOLIO".roles table & private Vercel Blob store the live Job Board reads from
Apply-to-Jobs Pipeline
A 5-stage scrape → describe → tailor → render → sync pipeline that scrapes Apple and NVIDIA job postings, tailors a résumé per role through a bounded-concurrency Gemini pipeline with quota-vs-rate-limit-aware backoff, and syncs into the same Neon table and Blob store this portfolio's Job Board already serves from.
Operator (CLI)
npm run extract
Triggers the 5-stage pipeline: scrape → describe → tailor → render → sync.
Apple + NVIDIA Scrapers
TypeScript · dual source
Scrapes Apple (GPU/TPU/Infrastructure/Backend keyword filter) and NVIDIA (Eightfold's /api/pcsx search), disambiguated by a company column with disjoint job_id spaces (Apple \d+-\d+, NVIDIA nvidia-<id>).
Render Pipeline
Markdown → PDF
Renders every successfully tailored résumé to PDF and uploads to Blob, overwriting the same stable pathname on re-tailor.
Gemini Draft + Correction
gemini-3.1-flash-lite draft · 4-model correction chain
Drafts every résumé with one primary model; validation failures escalate through a separate 4-model correction chain, capped at 2 retries per role.
Neon "PORTFOLIO".roles
Shared Postgres table
Same table src/lib/db/jobs.ts reads. Each row moves through three states — discovered (description NULL) → described (resume_pdf_path NULL) → synced (resume_pdf_path set).
Vercel Blob (private)
Shared blob store
Same private store src/app/api/jobs/resume/route.ts streams from.
- Why fallback by quota, not capability
- Gemini tracks quota independently per model. FallbackChainController advances a chain's pointer only when classifyGeminiError judges a failure genuine daily-quota exhaustion, not a transient rate limit — the draft pass currently runs a single model (its fallback partner was pruned after retirement), so the real redundancy lives in the separate correction chain used only to fix validation failures.
- Postgres as the sole state store
- No local checkpoint file — Postgres is the pipeline's only state store. Each role's row moves through three states in order: description IS NULL (freshly discovered stub) → description set but resume_pdf_path IS NULL (described, not yet tailored) → resume_pdf_path set (fully synced). Every stage writes its transition straight to the row, so a crash or quota wall mid-run loses at most one in-flight write.
- Why sync into existing infra
- Writes land in the same Neon "PORTFOLIO".roles table and private Blob store the Job Board already reads/serves — no parallel storage layer. /jobs renders fully dynamically per request (no ISR/revalidate window), so a newly-synced row is visible on the very next page load.
- Why 4x/day, not continuous
- A GitHub Actions cron re-runs the full pipeline four times a day (roughly 5am/9am/5pm/9pm Pacific), trading real-time freshness for a predictable, low-volume cadence that stays comfortably inside Gemini's daily quota.
- Live pipeline snapshot (as of 2026-08-07)
- 1,168 roles synced across Apple and NVIDIA, 246 carrying a tailored résumé (21%) — Apple: 181/364 synced, NVIDIA: 65/804 synced. A static, dated snapshot — this card is authored at build time, not re-queried on page load. For current numbers, ask the chat agent: it queries the same table live via getPipelineStats().
Questions about the architecture
Why is this whole platform free to run?
The zero-cost constraint is a deliberate design requirement, not an accident: this site runs entirely on free tiers — Vercel's Hobby plan (a rolling 24-hour window, 100 deploys/day), a GitHub Actions cron running every 30 minutes as the real scheduler (Vercel's free cron caps at once a day), and free GPU compute tiers on Hugging Face (ZeroGPU today, Colab T4/TPU next) that need no credit card. Staying inside those limits is itself the engineering exercise.
Can I reuse this architecture for my own project?
Yes — the site is built to be copyable: every layer (frontend, backend, model serving, data, ops) runs on a free tier, so nothing here depends on a budget you don't have. Fork the repo and read the version log below for the reasoning behind each piece.
How does the stack break down — frontend, backend, model, data, ops?
GitHub is the data warehouse and logic engine (ingestion + a 3-tier raw/staging/production lifecycle), Vercel is the frontend (Next.js 16, React 19, TypeScript), Hugging Face is the model/inference layer, and Neon Postgres plus Vercel Blob are the data layer the Job Board and résumé downloads read from. See the observability answer below for what "ops" concretely means today.
What monitoring and observability actually exists today?
Structured JSON logs, not a dashboard. The /api/jobs and /api/chat routes each generate a correlation ID per request (crypto.randomUUID()), log structured JSON around it, and return it in an X-Correlation-ID response header so one request can be traced through logs. There is no APM or tracing UI yet — that gap is called out here on purpose rather than overstated.
What is the résumé-tailoring agent and job board pipeline?
A separate external pipeline, apply-to-jobs, runs five stages — scrape, describe, tailor, render, sync — on a GitHub Actions cron four times a day. It tailors a résumé per role with a bounded-concurrency Gemini pipeline (with quota/rate-limit-aware backoff), checks every tailored résumé against a golden-dataset QA rubric, and syncs into the same Neon Postgres table and Vercel Blob store this site's Job Board reads live.
Where's the code for the LinkedIn automation and the other lightweight agents?
Three standalone open-source repos come out of this same lineage: agentic-writer (content generation tooling), linkedin-content-loop (the LinkedIn posting automation — the "LinkedIn CI/CD"), and resume (résumé-tailoring tooling, built alongside the pipeline that feeds the Job Board). All three were extracted and hardened into independent, distribution-ready tools.
Why build all of this instead of just applying to jobs manually?
Because there's no such thing as job security — treating the job search itself as a system to design, automate, and iterate on is the point. This portfolio is a verification mechanism for engineering aptitude as much as it is a tool: every piece, from the tailoring pipeline to this changelog, is meant to demonstrate the same judgment an employer is evaluating.
What changed most recently, and how do I track changes over time?
Each project section above has its own version history, in the sidebar's Projects group — Job Board, Hugging Face/ZeroGPU, Study Plan, Chat/RAG Agent. This answer intentionally doesn't restate any entry, so it can't go stale the next time something ships.