Spec #11 — Résumé / CV Document Rendering (ATS-safe PDF)
Status: Done (shipped 2026-06-15) · Owner: Hust · Effort: L · Phase 2 · Depends on: #5, render service (shared with #10)
1. Problem & user value
Hust parses CVs today but doesn't produce them. The vision: turn the user's structured profile into a real, ATS-hardened résumé (HTML→PDF), tailored per role, exportable. "A file, not advice." (LaTeX output is a fast-follow.)
2. Scope
In: render the user's cvParsedData (+ per-role tailoring from #3)
into an ATS-safe PDF via the shared render service; an ATS sanitizer + section-order
validator; versioned artifacts.
Out: LaTeX (fast-follow); the cover letter (#10, shares the render service); writing-style voice (#14).
3. Design
- Templates: a small set of ATS-safe HTML templates (single-column, standard headings, no tables/columns that break parsers).
- Two pure guards (ported as our own utilities):
normalizeTextForATS— Unicode/character sanitizer for ATS compatibility (with telemetry of replacements).validateCvSectionOrder— asserts the section order; throws on divergence (deterministic, unit-tested).
- Tailoring: optional per-role emphasis from the #3 customization block; never invents
experience (
assertNoInvented). - Output: PDF via the shared render service (#10); stored in the
documentstable + Supabase Storage; versioned.
4. Data / API
- Reuse
documentstable (kind =resume) + Storage. Guards inpackages/cv-parseror a newpackages/documents.resumeBuildertool upgraded to render real files.
5. Plan & tasks
- ATS-safe HTML template(s).
normalizeTextForATS+validateCvSectionOrder(pure, unit-tested with telemetry counters).- Render path (shared service) → PDF →
documents/Storage; versioning. - Optional per-role tailoring from #3;
assertNoInvented. - UI: build/preview/download; version history. (LaTeX export = follow-up task.)
- Tests: sanitizer cases, section-order throw, E2E build→download PDF.
6. Acceptance
- A user generates an ATS-safe résumé PDF from their profile; the sanitizer + section-order validator are unit-tested; per-role tailoring never invents experience; CI green; zero competitor references.
Implementation (shipped)
Built on the #5 structured-output contract. Delivers grounded per-role résumé tailoring with a server-rendered, ATS-friendly PDF download.
- AI tool —
tailorResume(packages/ai/src/tools/tailor-resume.ts,tailorResumeTool): pulls the user'scvParsedData/skills + the target job, callsgenerateValidatedObjectto produce a grounded tailored résumé (rewritten summary, 3–6 achievement bullets, JD keywords, ATS tips), runs the #6 no-invent audit, and returns a validated artifact. Registered astailorResumein the orchestrator (packages/ai/src/agents/orchestrator.ts, exported frompackages/ai/src/tools/index.ts). - Structured schema (
packages/ai/src/structured/schemas/resume.ts):resumeDraftSchema(LLM output) +resumeSummarySchema+resumeArtifact(defineArtifact("resume", …),RESUME_SCHEMA_VERSION = 1) — the whitelisted machine surface. - No-invent guard (
packages/ai/src/policy/assert-no-invented.ts,assertNoInvented): advisory grounding check that flags ungrounded proper nouns / years / numbers against the real CV facts. This is the shipped form of the spec'sassertNoInvented(it flags rather than throws). - Resume guidance helpers (
packages/ai/src/tools/resume-builder.tsresumeBuilderTool+ purepackages/ai/src/tools/resume-helpers.ts):extractAtsKeywords,findSkillOverlap,getFormatTips— the latter encodes the ATS-safe formatting rules (single-column, standard headings, no tables/graphics, standard fonts) as guidance. - Server PDF route —
POST /api/documents/pdf(apps/web/app/api/documents/pdf/route.ts): auth +exportrate-limit gated, Node.js runtime, renders any advisory artifact to a PDF download via@react-pdf/renderer. - PDF template (
apps/web/lib/pdf/artifact-document.tsx,ArtifactDocument): pure-JS, serverless-safe (no headless Chromium), A4 single-column ATS-friendly layout, shape-agnostic across artifact types. - UI —
ArtifactCard(apps/web/components/canvas/artifact-card.tsx): renders the tailored résumé on the jobs canvas with a grounded/flagged badge, Copy-as-text, and a Download-PDF button (calls/api/documents/pdf). - Tests:
packages/ai/src/tools/tailor-resume.test.ts,packages/ai/src/structured/schemas/resume.test.ts,packages/ai/src/tools/resume-helpers.test.ts, and the no-invent cases inpackages/ai/src/policy/policy.test.ts.
Intentionally deferred / divergent from the original design
- Persistence + versioning: the PDF is generated on demand and streamed as a download — there
is no
documentstable, no Supabase Storage upload, and no version history yet. (Nodocumentsschema exists inpackages/db/src/schema/.) - Named pure guards
normalizeTextForATS/validateCvSectionOrder: not built as discrete utilities. ATS safety is instead delivered by the single-column@react-pdftemplate plus thegetFormatTipsguidance; a Unicode sanitizer and a throwing section-order validator remain future enhancements. - LaTeX export: still a fast-follow (was out of scope here).