cover-letter-generation
User needs to generate a personalized cover letter for a specific job application
Changelog
260428: reclassify pattern label + outcome feedback
- Reclassified self-improvement pattern P2 (Skill Crystallization) to P4 (Compiler Wiki) per consolidated vault audit 2026-04-27.
- Added Outcome Feedback section reading from ~/vault/meta/outcome-feedback.jsonl (Phase 3B)
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 cover letter generation that produces personalized, recruiter-optimized letters as part of the projects/jobs-apply/_index automated application pipeline. The CoverLetterGenerator class (115 lines) takes a candidate profile, target job listing, and optional company context, then calls DeepSeek v3 via OpenRouter to produce a structured JSON response containing the full letter text and 3-5 extracted key talking points.
The system prompt enforces professional-but-authentic tone, company-specific references, quantified achievements, and a strict 400-word ceiling. Letters follow a four-beat structure: opening hook (never “I am writing to apply for…”), relevant experience with numbers, a specific value proposition tying the candidate to the role, and a confident call to action.
This skill runs inside the TailorPipeline during stage 3 (TAILOR) of every channel iteration. The generated cover letter is stored as coverLetterText on the applications table row and is available for form-fill injection during submission.
Interface
Trigger: Pipeline reaches TAILOR stage for a 70+ scored job, or manual generation requested.
Inputs:
UserProfile: name, years of experience, skills array,resume_json(experience with highlights, education, certifications). The prompt extracts the top 3 most recent roles and formats their titles, companies, date ranges, and highlight bullets as context.JobDetails: title, company, location, remote type, description (truncated to 1,500 chars), requirements array.CompanyInfo(optional): name, industry, freeform notes. When provided, a--- COMPANY CONTEXT ---section is appended to the prompt for deeper personalization.
Outputs (CoverLetterOutput):
content: string: the full cover letter, plain text (no markdown headers).keyPoints: string[]: 3-5 highlights the letter emphasizes, suitable for recruiter quick-scan or dashboard display.usage: AIChatUsage: token counts, model used, duration. Cost tracked viaestimateCost()and emitted as acover_letterusage event.
Configuration:
- Temperature: 0.6 (moderate creativity: enough variation to avoid template feel, enough consistency to stay professional).
- Max tokens: 4,096 (generous ceiling; actual output is ~300-400 words).
- Model: DeepSeek v3 via OpenRouter (same provider as skills/resume-tailoring).
Provenance
Developed alongside skills/resume-tailoring as the second half of the TAILOR stage in the projects/jobs-apply/_index pipeline. Both skills share the same OpenRouterProvider instance and follow the same pattern: system prompt with guidelines, structured user prompt with candidate + job sections, JSON-only response format.
The cover letter generator was built after observing that many ATS portals (Greenhouse, Lever, Direct career pages) include an optional or required cover letter field. Without one, applications appeared lower-effort. The generator ensures every submission includes a tailored letter without manual writing.
Key integration point: TailorPipeline.tailorJobsBatch() calls coverLetterGen.generate() immediately after tailor.tailor(), then stores the result on the application record for the submit stage to inject into forms.
Related files:
packages/ai/src/cover-letter.ts: the generator class and prompt builderpackages/ai/src/__tests__/cover-letter.test.ts: 6 test cases covering output shape, prompt content verification, company context inclusion/omission, and empty experience graceful handlingpackages/engine/src/pipeline/tailor.ts: pipeline integration (lines 139-142, 161)packages/ai/src/resume-tailor.ts: sibling skill, same architectural pattern
Usage Notes
- Specific beats generic. The prompt injects the candidate’s actual role titles, company names, and quantified highlights: not a generic summary. Letters that reference “Designed ETL pipeline processing 2M rows/day” outperform “experienced in data engineering” every time.
- Quantified beats vague. The system prompt explicitly instructs the model to weave in numbers, percentages, and dollar amounts from the candidate’s experience highlights. If the source profile lacks quantified achievements, the output quality degrades noticeably.
- Short beats long. The 400-word cap respects recruiter scanning patterns. Most recruiters spend 6-7 seconds on a cover letter. The four-paragraph structure (hook, experience, value prop, CTA) puts the strongest signal in the opening and closing lines where eyes land first.
- Company context is high-leverage. When
CompanyInfois provided (industry, notes about recent news or culture), the model produces noticeably more personalized openings. The projects/jobs-apply/_index pipeline currently passes company info when available from thecompaniestable enrichment (seescripts/enrich-companies.ts). - Cost is negligible. At DeepSeek v3 pricing (~$0.001 per call), generating a cover letter for every 70+ match adds minimal cost to the pipeline. Usage is tracked per-call and surfaced in the dashboard cost view.
- No re-scoring gate. Unlike resume tailoring, cover letters are not re-scored before submission. The letter is always used as-is. Quality is enforced entirely through prompt engineering.
See also: skills/resume-tailoring, topics/interview-rate-optimization.
Quality Checks
- Output length 300–450 words.
wc -w cover.mdin range; shorter = generic, longer = overindexed. - ≥3 concrete examples or bullet points. Generic letters have 0–1; tailored letters earn their keep with specifics.
- Company name referenced ≥3 times. Anchor the letter to the target; generic letters fail this check.
- No em dashes. Style constraint:
grep -c ': ' cover.mdreturns 0. - Opens with a specific hook. First sentence must not start with “I am writing to…” or “Please accept my application…”.
- Closes with forward-pointing action. Final paragraph proposes next step (call, portfolio link, interview).
Visual Enrichment
| Medium | Type | Description |
|---|---|---|
| Figma | Flowchart | Generation pipeline: profile -> LLM -> letter -> PDF |
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 (interview/rejection/screening/offer) - Group by cover letter features: length (word count bucket), tone (formal/conversational), keyword density, structure (3-paragraph vs 4-paragraph)
- Surface which features correlate with interviews vs rejections
- Report format only: human reviews correlations before adjusting templates
Key metrics to track:
- Interview rate by cover letter length bucket (300-350 / 350-400 / 400-450 words)
- Interview rate by company-name mention count (1-2 / 3-4 / 5+)
- Interview rate by opening style (specific hook vs generic)
This is a report, not automated optimization. The Huang Constraint (self-correction without external feedback degrades performance) applies here: the external signal is real interview outcomes from Gmail crawl, not LLM self-evaluation.
Self-Improvement Cross-Reference
Pattern 4 (Compiler Wiki): vault notes compiled into cover letter content is compilation. No GEPA cycle exists; letters are used as-is without iterative skill refinement. Reclassified from P2 (Skill Crystallization) per consolidated vault audit 2026-04-27. For the master reference on all 6 self-improvement patterns, see skills/self-improving-agent-patterns.