ResumeAdapter
The ResumeAdapter Method
Updated 2026-06-03

A resume is not a document.
It is structured data.

Engineering, not prompting

Most AI resume tools stuff your resume and a job description into one large prompt and render whatever comes back. Run the same input twice and you get two different resumes. We built the opposite: typed models, deterministic comparison, and atomic rewrites validated at every model boundary. Here is exactly how the pipeline works.

See the pipeline in actionTyped modelsDeterministic scoringValidated rewrites
By the numbers
Pipeline stages
5
Parse to rewrite
LLM-output validation
Zod
At every model boundary
Parse confidence
8 + 4
Positive and penalty signals
Gap matching
Deterministic
Code-based, no LLM

The quick answer

Is ResumeAdapter just a ChatGPT wrapper?

No. A ChatGPT wrapper stuffs your resume and a job description into one giant prompt and renders whatever the model returns, which is why the same input gives a different resume each time, with invented titles, dropped certifications, and fabricated metrics. ResumeAdapter treats a resume as structured data instead. It parses your resume into a typed model, extracts the job description into a second typed model, finds keyword and skill gaps with deterministic code, scores them with plain math, generates a structured rewrite plan you can review, and only then runs small rewrites that are each validated against a schema before they are accepted. The model writes individual bullets; it does not run the pipeline. The result is reproducible analysis, explainable changes, and contained failure. Run it on your own resume to see the structured output for yourself. Run your resume through it.

Treat a resume as text and you are at the mercy of whatever the model happened to roll on temperature that day. Treat it as a typed object, with parsed fields, validated schemas, and deterministic comparison logic, and most of the failure modes disappear: invented job titles, dropped certifications, fabricated metrics.

The reframe sounds obvious in retrospect. But almost every tool in the category still operates on text blobs, because building the structured layer is harder and slower than shipping a prompt. The five components below are how we built that layer. Each does one thing, has its own schema, and can be tested in isolation.

The split that matters: matching and scoring run as deterministic code, so the analysis is reproducible. The model structures the raw inputs and writes the rewrites, and every model output is validated by a schema before it flows downstream.

The pipeline

Five components.
Each one testable in isolation.

  1. 01
    CRDM

    Canonical Resume Data Model

    The single structured representation of a resume. Every parsed CV becomes a typed object with contact details, experience entries (each with its own bullets, optional achievements, and skills), education, and categorized skills.

    How it is built

    Validated by a Zod schema before it enters any downstream step. The skills are also flattened into one list for fast matching, and the source file is hashed so the same resume is recognized across runs. If the parser cannot extract a field with confidence, that is recorded, not silently filled.

  2. 02
    CJDM

    Canonical Job Description Model

    The same idea applied to the job description: title, seniority, industry, required skills, preferred skills, required qualifications, keywords, responsibilities, years of experience, and recruiter signals, all as typed fields.

    How it is built

    Extracted with a structured-output call whose response is constrained to a Zod schema. Once both sides are a CRDM and a CJDM, comparing them is a data problem, not a prompting problem.

  3. 03
    Gaps

    Deterministic gap detection and scoring

    The found and missing keywords and skills, plus the ATS-style score. Same resume and job description in, same gaps and score out.

    How it is built

    Keyword and skill matching runs as a pure function, with no model in the loop, so the same resume and job description produce the same gaps every time. The score is computed deterministically from that structured comparison. Reproducibility is the point: ask why a gap was flagged and the answer is a comparison you can re-run, not a prompt that rolled differently that day.

  4. 04
    RPG

    Rewrite Plan Generator

    A blueprint, not the rewrite itself: which bullets to rewrite, which skills to surface, where to add quantification, which generic verbs to replace, and which formatting constraints to enforce.

    How it is built

    Produced and stored as a structured plan that is separate from generating any text, so it can be shown to you before a single word is rewritten. Smaller gap sets use a pure plan builder; larger ones use a model-assisted planner. Either way the plan is the contract the next step executes.

  5. 05
    MRC

    Modular Rewrite Chain

    Where the model finally writes. Instead of one prompt that rewrites the whole resume, the chain runs small, scoped steps: rewrite this summary, rewrite this bullet, inject these skills.

    How it is built

    Each step has its own minimal prompt, returns JSON validated by a Zod schema, and runs in isolation. If one bullet fails, that bullet keeps its original text and the rest proceed, so a single bad step never corrupts the whole resume. Skill injection is a pure step with no model at all.

Anonymized schemas

The typed boundaries,
simplified but faithful.

Representative shapes from the real pipeline. Field names are trimmed for readability, but the structure and the validation are what actually run.

CRDM: the resume as a typed object
const CRDM = z.object({
  version: z.number(),
  profile: z.object({
    personalInfo: z.object({ fullName, email, phone, location, summary }).partial(),
    experience: z.array(z.object({
      company: z.string(), title: z.string(),
      startDate: z.string(), endDate: z.string(),
      bullets: z.array(z.string()),
      achievements: z.array(z.string()).optional(),
      skills: z.array(z.string()).optional(),
    })),
    education: z.array(/* institution, degree, dates */),
    skills: z.object({ technical, soft, certifications, languages }).partial(),
  }),
});
Gap detection: a pure function
// Pure function. Same input, same output. No model in the loop.
function detectGaps(crdm, cjdm): {
  foundKeywords: string[];  missingKeywords: string[];
  foundSkills: string[];    missingSkills: string[];
  stats: { keywordMatchRate: number; skillMatchRate: number };
}
One atomic rewrite, validated
// Every rewritten bullet is validated before it is accepted.
const RewrittenBulletSchema = z.object({
  rewrittenText: z.string(),
  keywordsInjected: z.array(z.string()),
});

const result = await structuredLLM.invoke(prompt);   // one bullet, one prompt
const bullet = RewrittenBulletSchema.parse(result);  // throws on bad output

The non-negotiable rule across the whole pipeline: every model response is validated by a Zod schema before it enters the next step. A malformed response becomes a caught error, not a corrupted resume. That single discipline is what lets the rest of the system keep its guarantees.

Why it matters

Three things change
once you build it this way.

01

Reproducibility

Because matching and scoring are deterministic, the same resume and job description produce the same gaps and the same score every time. The only stochastic piece is the rewrite text inside each step, and even that is bounded by a schema.

02

Explainability

Every change maps back to a specific gap, a specific plan item, and a specific rewrite step. The plan is shown before any text is generated, so you can preview and approve changes instead of trusting a black box.

03

Safe failure

When something goes wrong inside a giant prompt, the whole resume is suspect. When a single atomic step fails validation, that one bullet is kept as-is and the rest of the chain is untouched. Failure is contained, not contagious.

Before the pipeline: parsing

The parser is the
hardest part, so it reports on itself.

Everything downstream depends on extraction quality. A bad parse means a bad analysis. So the parser does not just return text; it returns a confidence report and routes hard cases automatically.

Multi-format

PDF, DOCX, and images, each handled where it belongs.

Text-layer PDFs are parsed in the browser with pdfjs and DOCX files with mammoth. Images are sent to a server route for OCR. Nothing leaves the device until a format genuinely needs server processing.

Confidence report

Eight positive signals, four penalty signals.

Every parse returns an extraction report: points for a detected email, phone, recognized sections, experience, education, skills, dates, and bullets, minus penalties for too little text, missing sections, too few words, or no contact block. You see how confident the extraction was, not a silent guess.

Auto-OCR

Scanned PDFs are detected and rerouted.

When a PDF returns almost no text (under one hundred characters) and shows no real structure, it is treated as a scan and routed to server-side Google Vision OCR automatically, rather than handing the user an empty result.

FAQ

How the engine works, answered

The questions people ask about whether this is really different from a prompt, and what the structure buys you. Answers are byte-identical to the FAQPage JSON-LD, because AI engines that read HTML and AI engines that read schema should see the same text.

Is ResumeAdapter just a ChatGPT wrapper?

No. A ChatGPT wrapper stuffs your resume and a job description into one large prompt and renders whatever comes back. ResumeAdapter parses your resume into a typed model, extracts the job description into another typed model, finds gaps with deterministic code, generates a structured rewrite plan, and only then runs small, schema-validated rewrites. The model writes individual bullets; it does not run the pipeline.

Does ResumeAdapter use AI to rewrite my resume?

Yes, but only in scoped, validated steps. The keyword and skill matching and the ATS-style score are deterministic code with no model involved. A model is used to structure the inputs and to write each bullet or summary, and every one of those outputs is validated by a Zod schema before it is accepted.

Will it invent jobs, degrees, or metrics?

The architecture is built to prevent that. Rewrites are atomic and scoped to your real content, each step is validated against a schema before it is used, and a failed step keeps your original text instead of substituting something invented. No system can promise zero errors, but bounding each step to validated, content-specific edits removes the most common path to fabrication.

Is the analysis reproducible?

The matching and scoring are. Run the same resume against the same job description twice and you get the same found and missing keywords, the same skill gaps, and the same score, because that layer is pure code, not a prompt. Only the rewrite wording varies between runs, and even that is constrained by the schema each step must satisfy.

Can I see why a change was made?

Yes. Each rewrite traces back to a gap the analysis found and an item in the rewrite plan, and the plan is generated separately from the text and can be shown before anything is rewritten. When you ask why a bullet changed, the answer is a chain of structured steps, not an opaque model output.

Why validate every model response with Zod?

Models return malformed JSON, miss fields, and occasionally output prose where a typed value was expected. Validating each response against a Zod schema at the boundary turns those failures into a caught error instead of letting corrupted data flow into your resume. It is the rule that lets the rest of the pipeline keep its guarantees.

How does it handle scanned PDFs?

It detects them. When a PDF yields almost no extractable text and no real structure, ResumeAdapter treats it as a scan and routes it to server-side OCR with Google Vision, rather than returning an empty parse. Every parse, scanned or not, comes with a confidence report so low-quality extractions are visible up front.

See the structured output

Read the method,
then run it on your resume.

Get your ATS-style score, the gaps the deterministic layer found, and a rewrite plan you can review before any text is generated. Free to scan; no signup to see the score.