The resume is a document people rewrite from scratch every time they need one.
Three problems sit on top of each other. Writing a CV from zero is slow and painful; every application wants a slightly different shape; and the record itself decays — a project that mattered in March is forgotten by October, and the next rewrite begins from a thinner memory than the last. The usual tools address the first problem and make the other two worse: a PDF template is a dead artifact the moment it is exported.
I wanted a platform where the resume was not a document at all, but a living record — one the user keeps current through conversation, and one the system can project into a tailored CV whenever a specific posting demands one. The ground truth lives in the database. The PDF is a rendering.
The goal was to stop asking people to remember their own careers from a blank page.
A hybrid data shape, an AI-first editor, a credit-gated generator with a truthfulness guard.
The product settles into three phases — Onboarding, Applications, Maintenance — and a handful of architectural decisions hold them together:
- Hybrid data model. Each resume section — experiences, education,
skills, certifications, projects, publications, honors — is a row with
structured columns only for what needs querying or display (company,
role, dates) and a Markdown
contentfield for everything else. The structure carries the facts; the Markdown carries the narrative the AI reads and edits. - AI-first editing, Markdown as escape hatch. The primary UX is a conversational agent that updates sections through tool calls. A raw Markdown editor is always one click away for users who want it. The chat is the default; the text field is the floor.
- A parsing pipeline that imports once. Uploaded PDF →
ParseResumeJob→ResumeParseAgentturns the resume into HTML by section → a second agent structures it into database rows → the user reviews. Onboarding is the moment the record comes alive. - A generation pipeline that refuses to invent.
FetchSourceData → GenerateCv → GuardInstructions → SanitizeCvHtml → SaveGeneration. A dedicatedGuardAgentvalidates inputs and outputs so the model cannot add a credential the user does not have. Credits are debited insideApplication::startGeneration()with transactional safety. - Specialized agents, structured output.
ResumeParseAgent,ResumeStructureAgent,CvGenerationAgent,GuardAgenteach own a narrow job with strict schemas — Gemini 2.5 Flash for parsing, Claude Haiku 4.5 for generation, OpenRouter as the provider.
The stack is Laravel 13 on PHP 8.4 with Fortify, Spatie Media Library, and Wayfinder for typed routes; Inertia v3 + React 19 + Tailwind v4 on the front end; Pest 4 with the browser plugin for end-to-end coverage across onboarding, the application wizard, and CV preview.
Still being built — but the shape is holding.
ResumesKit is in active development and has not yet been opened to
users. The onboarding pipeline ingests a real PDF and produces
structured rows; the resume sections are CRUD-complete behind the
conversational agent; the application wizard generates a tailored CV
from a pasted posting against a chosen template, debits a credit, and
renders a preview. The GuardAgent has refused every attempt so far to
put something in a CV that was not in the record.
The part that already feels right is the boundary between the structured columns and the Markdown. The AI does not have to reason about table schemas; the UI does not have to render arbitrary prose as if it were a field. Each half does the work it is good at.
What I have learned so far — knowing the real lessons arrive with users.
The first thing that keeps proving itself is the decision to let the agent be the primary editor. Every time I have been tempted to add a form, the form has been worse than the conversation for the user and more brittle for me to maintain. The Markdown escape hatch is enough for the long tail; the chat handles the head.
The second is the value of narrow agents with strict schemas.
CvGenerationAgent does one thing; GuardAgent does one thing; the
onboarding agents do one thing each. When something goes wrong, I can
point at the agent and the tool call. Monolithic prompts would have
hidden the failure in a thousand tokens.
The third, still forming, is about truthfulness as an architectural property rather than a prompt. Asking a model nicely not to lie is not a strategy; routing its output through a validator that has read the record is. The guard is cheap. It pays every day.
The honest caveat: this project has not yet met its users. The lessons above are the ones engineering can teach on its own. The lessons that matter most — what non-technical job seekers actually want from a living record, how they want to be nudged to keep it alive, what a tailored CV needs to look like to earn the next interview — are waiting on the other side of the first real cohort.