▌ ResMyCV — ATS Optimisation Feature.md
▒ PATH:
▒ SIZE: 3.5 KB
▒ MODIFIED: 2026-06-02 03:12
Projects/ResMyCV — ATS Optimisation Feature.md▒ SIZE: 3.5 KB
▒ MODIFIED: 2026-06-02 03:12
# ResMyCV — ATS Optimisation Feature
**Date:** June 1, 2026
**Status:** Deployed to Railway (rezmycv.com)
---
## What was built
Two-step CV creation flow replacing the previous single-step tailor:
1. **Step 1 — Tailor CV** (`/cv/tailor`)
- Takes: profile CV + job description + gap answers
- Generates: base tailored CV with summary, experience, skills, education
2. **Step 2 — Match ATS Keywords** (`/cv/optimise`)
- Takes: tailored CV + job description + ATS keywords + requirements + gap answers from Supabase
- Rewrites: all fields (personal, summary, experience, skills, education) for ATS keyword match
- Draws from: profile CV, tailored CV, gap answers (3 sources)
- Rule: summary MUST always be present — never null, never empty, never removed
---
## Key files changed
| File | Change |
|------|--------|
| `services/optimise.py` | New — ATS optimisation service, all fields eligible, mandatory summary rule |
| `services/cover_letter.py` | Added humanised sentence length mixing |
| `app.py` | Added `/cv/optimise` route, passes gap_answers from Supabase user_cvs |
| `templates/cv_editor.html` | Added purple "Match ATS Keywords" button + JS handler |
| `templates/cv_editor.html` | Fixed nl2br → `replace('\n','<br>')\|safe` |
| `templates/dashboard.html` | "Delete CV" → "Delete Profile CV", added how-it-works info box |
| `templates/dashboard.html` | How-it-works box: one job at a time, download CV+CL then delete to start fresh |
| `templates/cover_letter.html` | nl2br filter fix |
---
## ATS Optimise prompt rules
- All fields eligible for rewriting (personal, summary, experience, skills, education)
- Only factual core must stay truthful (real jobs, real dates, real titles)
- Gap answers enrich experience bullets
- **MANDATORY OUTPUT RULE:** summary must ALWAYS appear — 2-3 sentence first-person professional summary
- Safeguard in code: if AI returns empty summary, restored from original CV before returning
---
## Cover letter humanisation
Added to prompt:
- Short punchy sentences (3-6 words) mixed with longer flowing ones (15-20 words)
- Natural human rhythm — not uniform sentence length
- Keep cover letter grounded in CV + gap answers facts
---
## Bug fixed: summary missing from optimised CV (June 1, 2026)
**Symptom:** AI returned valid JSON with `summary` field, debug log confirmed it was present, but template showed no summary.
**Root cause:** `_prepare_cv_context()` only copied summary into `personal` when `personal` was built from scratch (lines 51-60). When `personal` already existed from the AI output, the summary at top level was never copied over.
**Fix (app.py line 65-71):**
```python
if "personal" not in ctx:
ctx["personal"] = {...}
if ctx.get("summary"):
ctx["personal"]["summary"] = ctx["summary"]
else:
# personal already exists from AI output — ensure summary is also present in it
if ctx.get("summary") and not ctx["personal"].get("summary"):
ctx["personal"]["summary"] = ctx["summary"]
```
**Debug added:**
- `services/optimise.py`: print statements for raw response type, parsed keys, summary value, fallback restoration
- `app.py /cv/optimise`: app.logger.info of returned keys and summary value
---
## Known issues fixed
- `nl2br` Jinja2 filter didn't exist on Railway → replaced with `|replace('\n','<br>')|safe`
- Summary missing from optimised CV → `_prepare_cv_context` now handles both cases (personal built from scratch AND personal already from AI output)
---
## GitHub
Repo: `ncik8/TailorMyCV`
Railway auto-deploys on push to main.