resume-tailoring
User needs to tailor a resume for a specific job posting, optimizing for ATS and recruiter scanning
Changelog
260428: shadow-mode gate + outcome feedback
- Replaced disabled re-scoring gate with shadow-mode specification (logs delta to JSONL, no blocking)
- Exit criteria: 50 data points → median delta + outcome correlation analysis
- Phase 1A of vault audit remediation (Finding 1)
- Added Outcome Feedback section reading from ~/vault/meta/outcome-feedback.jsonl (Phase 3C)
260420: multiple edits
- v_migrate: Changelog migrated from table to YYMMDD H3 format per versioning-standard rule 2 (V1.6 of skills upgrade plan)
- v6: Added license, sources per V6.1/V6.2 of skills upgrade plan.
- v1.5: Added
## Quality Checkssection per V1.5 of ~/vault/plans/2026-04-20-vault-skills-upgrade-plan.md
260403: Added Visual Enrichment section + self-improving-agent-patterns cross-reference
260331: Initial creation
Description
AI-powered resume customization that rewrites a candidate’s resume for each individual job application. The system takes structured resume data (JSON) and a target job posting, then uses an LLM to reorder sections, inject keywords from the job description, quantify achievements, and optimize formatting for Applicant Tracking Systems: all while preserving factual accuracy. No experience is fabricated; the LLM only rephrases, reorders, and emphasizes existing content.
The tailoring pipeline is one of five stages in the jobs-apply unified pipeline (DISCOVER -> MATCH -> TAILOR -> SUBMIT -> TRACK). A job that scores 70+ on J-Score v2 gets tailored in the same cycle it is discovered: no batching. The tailor stage produces both a tailored resume and a personalized cover letter, then renders them to PDF via Puppeteer before the submit stage takes over.
Pattern
-
Input assembly.
buildTailorPrompt()serializesresume_json(experience, education, skills, projects, certifications) and target job (title, company, location, description capped at 2000 chars, requirements). Contact info excluded from tailored output: renderer adds it from profile. -
LLM rewrite.
ResumeTailorcallsOpenRouterProvider.generateJSON()with system prompt (expert resume writer, seven guidelines: keyword match, quantified achievements, action verbs, ATS optimization, truthfulness, relevance ordering, concise bullets), temp 0.4, max 8192 tokens. Returns{ content: string, modifications: string[] }. -
Cover letter. In parallel,
CoverLetterGeneratorproduces a 3-4 paragraph letter at temp 0.6. Returns{ content: string, keyPoints: string[] }. See skills/cover-letter-generation. -
PDF rendering.
ResumeRendererconverts to ATS-friendly HTML (semantic sections, Georgia/11pt/Letter/0.5in margins) and launches headless Puppeteer. Output:data/resumes/resume_{jobId}_{timestamp}.pdf. -
Status flow. Job:
matched -> tailoring -> queued. On failure: rolls back tomatched. Duplicate check before tailoring (prevents re-PDF generation on retry). Platform batching:Promise.allSettledacross platforms, sequential within each platform batch. Cost: ~$0.002/pair at DeepSeek v3 pricing.
Interface
Trigger
The tailor stage activates automatically when a job reaches matched status with a J-Score >= 70 during a channel run cycle. It can also be invoked via the SaaS API (POST /api/jobs/tailor) for the agent-mode architecture.
Inputs
| Input | Type | Source |
|---|---|---|
resume_json | Record<string, unknown> | profiles.resume_json column: structured JSON with experience[], education[], skills[], projects[], certifications[] |
job_details | JobDetails | Converted from jobs table row via jobRowToDetails(): title, company, location, remote_type, description, requirements[] |
user_profile | UserProfile | Converted from profiles table row via profileRowToUserProfile(): name, years_experience, skills, resume_json |
Outputs
| Output | Type | Destination |
|---|---|---|
content | string | Tailored resume body (markdown-style formatting with sections and bullet points) |
modifications | string[] | List of specific changes made and why (e.g., “Moved Python experience to top: JD lists Python as primary requirement”) |
usage | AIChatUsage | Token counts + model + duration + generation ID: fed to cost tracking |
| PDF file | resume_{jobId}_{timestamp}.pdf | Written to data/resumes/, path stored in applications.tailored_resume_path |
coverLetterText | string | Stored in applications.cover_letter_text |
Provenance
Built for projects/jobs-apply/_index as part of the automated pipeline. Tailoring quality feeds the topics/interview-rate-optimization effort. Key experiment: experiments/jobs-apply/2026-03-15-linkedin-submission-rate-ratchet: ratchet across 7 runs improved submission rates 40% to 83%. Tailoring was not the bottleneck (anti-detection was), but robustness was required.
Re-scoring gate is in shadow mode (2026-04-28): after tailoring, J-Score v2 re-scoring runs but logs the delta instead of blocking. Shadow-mode data accumulates at ~/vault/meta/resume-tailoring-shadow.jsonl with schema {timestamp, application_id, original_score, re_score, delta, resume_hash}. No production behavior change: tailored resumes are still submitted regardless of re-score.
Exit criteria (after 50 data points):
- Median delta > -5 points: promote gate to blocking with -5 tolerance band
- Median delta < -5 with no outcome correlation: tune prompt first, then promote
- Median delta < -5 WITH outcome correlation: restore gate immediately at original threshold
See also: ~/vault/meta/outcome-feedback.jsonl (Phase 3 cross-reference for outcome correlation).
Related skills: skills/cover-letter-generation (parallel companion, same pipeline stage), skills/j-score-v2-matching (provides the 70+ floor that gates entry to tailoring).
Usage Notes
- The LLM never fabricates experience. System prompt states “only rephrase and reorder existing experience, never fabricate.” The
modifications[]output provides an audit trail for human review. - Three ATS factors: keyword density (weave job description phrases into bullets), section ordering (most relevant experience first), action verb + metric format (“Achieved X by doing Y, resulting in Z%”).
- Contact info is excluded from the tailored output. The renderer adds it from the profile record to prevent hallucination of phone numbers or email addresses.
- Duplicate prevention: if an application record already exists for the job, the pipeline skips retailoring and promotes directly to
queued. - Cost: ~$0.002 per tailor+cover-letter pair. Description capped at 2000 chars, cover letter prompt at 1500 chars. 500 resumes for $1.
- On API failure, status rolls back from
tailoringtomatchedfor retry.OpenRouterCreditsExhaustedErrorhalts all channels immediately.
Quality Checks
- ATS keyword density 1.5–3% for extracted target terms. Below = under-tailored; above = keyword-stuffed.
- All canonical sections preserved. Experience, Education, Skills headers present post-tailor; reorder allowed, removal not.
- Zero content hallucinations. Every claim in the tailored resume traceable to the source resume via grep. No fabricated certifications or dates.
- PDF renders without errors.
pdfinfo tailored.pdfexits 0;pdfimages -listshows no broken image refs. - Word count 400–800. Typical 1-page = 400–500, 2-page = 700–800. Outside = re-scope.
- Tailored keywords highlighted in audit.
score_audit.reasoningfield lists the specific keywords surfaced for this role.
Visual Enrichment
| Medium | Type | Description |
|---|---|---|
| R | CMP grouped bar | Keyword match % before/after tailoring |
| Figma | Flowchart | DISCOVER -> MATCH -> TAILOR -> SUBMIT -> TRACK |
Outcome Feedback
Data source: ~/vault/meta/outcome-feedback.jsonl (populated by ~/Documents/jobs-apply/scripts/outcome-feedback-export.ts after each Gmail crawl cycle).
Analysis protocol:
- Read
outcome-feedback.jsonlentries where outcome is known - Group by tailoring parameters: keyword injection count, section reordering, emphasis shifts, ATS score delta
- Surface which tailoring strategies correlate with positive outcomes
- Cross-reference with shadow re-score data (
~/vault/meta/resume-tailoring-shadow.jsonl) for Phase 4 gate restoration
Key metrics to track:
- Interview rate by ATS keyword density bucket (1-2% / 2-3% / 3%+)
- Interview rate by section reordering (yes/no)
- Correlation between shadow re-score delta and interview outcome (Phase 4 decision data)
This data feeds the Phase 4 gate restoration decision. If low re-scores correlate with rejections, the gate was catching real issues and should be restored immediately.
Self-Improvement Cross-Reference
Pattern 3 (Metric Ratchet): ATS score optimization is metric ratcheting on keyword match rates. For the master reference on all 6 self-improvement patterns, see skills/self-improving-agent-patterns.