Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ All notable changes to resumasher will be captured here. Format loosely follows

`Unreleased` covers work in progress on `main`. Tagged versions will appear below it once we start cutting releases.

## [0.4.7] — 2026-05-02

v0.4.7 teaches the tailoring sub-agent how to actually write a strong resume bullet.

The prompt up to this point told the LLM what *not* to do (don't fabricate, don't change identity, anchor every bullet to evidence) but never taught the *craft* of writing a strong bullet — no XYZ formula, no action-verb-first instruction, no "lead with outcome over activity," no weak→strong examples. With the loudest signals all pointing toward "stay honest" and nothing pulling the model toward "rewrite well," cautious models like Sonnet 4.6 produced lightly-edited source bullets rather than transformed ones. Reported by [@guiqvlaixi2164-max](https://github.com/guiqvlaixi2164-max) in [#65](https://github.com/earino/resumasher/issues/65): the tailored resume came out 3 pages long because resumasher "didn't summarize them" — the original verbose bullets passed through almost unchanged.

Fixed by adding three new "craft" sections to `scripts/prompts.py`'s `TAILOR_PROMPT`, sourced from a literature review across Harvard / MIT / Stanford career services, Bock's XYZ formula, Indeed, Jobscan, Teal, and Notch:

- **Bullet craft.** A composite formula (`[Strong past-tense verb] [scope] [outcome — quantified if honest, scoped/selectivity-substituted otherwise] by [method]`), 7 hard rules including a 12-item forbidden-openings list ("Responsible for", "Helped with", "Worked on", "Was involved in", "Tasked with", "Performed", "Participated in", "Engaged in", "Took part in", "Posted on", "Assisted with", "Duties included"), an explicit "do not invent percentages — substitute scope/frequency/selectivity/recognition" rule, and 7 curated weak→strong examples spanning SWE / ops / HR / marketing / leadership / finance.
- **Summary craft.** A trailer-shape teaching (identity sentence + 1-2 evidence sentences + optional ask) with a FORBIDDEN list covering generic adjectives ("passionate", "hardworking", "proven track record"), first-person pronouns, vague unsubstantiated claims, verbatim bullet repetition, every-skill-listed, AND meta-statements that claim the match instead of showing it ("directly matching X's mandate", "perfect fit for", "ideally suited for").
- **Skills section craft.** An explicit "the skills list is a set of claims about the candidate" rule with a worked WRONG/RIGHT example showing why a JD's BigQuery requirement does not justify adding BigQuery to a candidate's skills list when their resume only lists Snowflake.

Empirical validation via three iterative `claude -p` runs against a deliberately-weak fixture resume across Sonnet 4.6 and Haiku 4.5. The OLD prompt with Sonnet leaked 3 forbidden-opening violations through 13 bullets ("Performed", "Assisted with", "participated in"); the NEW prompt with Haiku produced 0 violations through 9 bullets. No fabricated metrics across any run. Two further rounds tightened the prompt against keyword-stuffing (model adding "demand-planning" to a bullet because the JD asked for it though the source resume said "merchandising") and meta-summary statements (model claiming alignment instead of showing it), and a third round closed the skills-section fabrication gap (model padding skills with BigQuery because the JD listed it though the candidate had only Snowflake).

Consciously deferred: the typography-side of #65 (smaller font / tighter leading / page-overflow auto-shrink) — if real-world testing on long source resumes shows the new prompt-side fix isn't enough, a renderer-side fallback is the natural follow-up. Also deferred: an explicit "3-5 bullets per recent role, 1-3 for older roles" budget rule from the literature, since the existing Length and Recency section already covers overall length and per-role caps can be added if needed.

No breaking changes; students on v0.4.6 upgrade in-place. Tailored resumes from the new prompt will read as more transformed and tighter than v0.4.6 outputs against the same source resume — no schema or filename changes, just better content.

### Changed

- **Tailoring prompt teaches resume bullet craft instead of just guarding against fabrication** ([#65](https://github.com/earino/resumasher/issues/65), reported by [@guiqvlaixi2164-max](https://github.com/guiqvlaixi2164-max)). The pre-existing `TAILOR_PROMPT` was heavy on guardrails (anchoring rule, no fabrication, no identity changes) and silent on craft — cautious models read this as "stay close to the source" and produced under-edited output. Adds three new sections to `scripts/prompts.py`: Bullet craft (composite `[verb][scope][outcome][method]` formula + 7 hard rules + 12-item forbidden-openings list + 7 weak→strong examples), Summary craft (trailer-shape teaching + FORBIDDEN list that includes meta-statements claiming alignment instead of showing it), and Skills section craft (explicit "skills are claims about the candidate" rule with WRONG/RIGHT example). Sourced from a literature review across Harvard / MIT / Stanford / Bock / Indeed / Jobscan / Teal / Notch. Empirically validated via six `claude -p` runs across Sonnet 4.6 and Haiku 4.5 against a deliberately-weak fixture: pre-fix Sonnet produced 3 forbidden-opening violations through 13 bullets; post-fix Haiku produced 0 violations through 9 bullets. Four failure modes uncovered and patched: under-editing (bullets passing through with weak verbs and "Responsible for" / "Helped with" openings), JD-vocabulary keyword-stuffing (model adding "demand-planning" because the JD asked for it though the source resume said "merchandising"), meta-summary claims ("directly matching X's mandate"), and skills-section fabrication (model adding BigQuery to skills because the JD asked for it though the candidate had only Snowflake). Each failure mode addressed with a concrete WRONG/RIGHT worked example in the prompt — pattern-matching teaches faster than rules. No schema or variable changes, so existing `test_prompts.py` schema-literal preservation assertions still pass. 459 deterministic tests still green.

## [0.4.6] — 2026-05-02

v0.4.6 makes the cover letter look like a real cover letter.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.6
0.4.7
209 changes: 209 additions & 0 deletions scripts/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,215 @@
**Do not invent experience, metrics, technologies, or project outcomes.** Do
not change the candidate's name, email, phone, LinkedIn, or location.

## Bullet craft (how to write each bullet)

The anchoring rule above governs WHAT goes in a bullet — only what the
source actually supports. This section governs HOW you write it. A bullet
that's anchored in real evidence but written weakly buries the candidate.
The dominant tailoring failure is not fabrication; it's under-editing —
lightly paraphrasing the source bullet instead of transforming it.

**The bullet shape:**

[Strong past-tense action verb] [specific scope or object],
[outcome — quantified if honest, scoped/selectivity-substituted otherwise],
by [method — tools, approach, or scale].

In plain English: lead with what the candidate DID, then what HAPPENED
because of it, then HOW they did it. Outcome before activity. The most
interesting fact goes at the front of the bullet — that's where the
recruiter's six-second scan will land.

**Hard rules — apply to every bullet:**

1. Start with a strong past-tense action verb (present tense only for
the candidate's current role). FORBIDDEN openings: "Responsible
for...", "Duties included...", "Helped with...", "Assisted with...",
"Worked on...", "Was involved in...", "Tasked with...", "Performed
...", "Participated in...", "Engaged in...", "Took part in...". These
describe being present, not contributing.
2. No first-person pronouns ("I", "my", "we"). Subject is implied.
3. Active voice, never passive. "Improved the pipeline" — not "The
pipeline was improved."
4. One sentence, one accomplishment. No semicolons stitching two
ideas into a megabullet — split or pick the stronger one.
5. 1 line preferred, 2 lines hard cap. ~15-25 words target.
6. Specific over general. "Reduced query latency 40%" beats "Improved
performance." "Led a 6-person team across 3 product surfaces" beats
"Led a cross-functional team."
7. Mirror the JD's terminology for the noun-phrase ONLY when the
candidate's evidence genuinely uses that terminology or a clean
truthful equivalent. Do not parrot JD phrases that don't match the
candidate's real work — that's keyword-stuffing, not tailoring.

INVALID (keyword-stuffing):
RESUME says: "daily SQL reporting on sales data for the
merchandising team."
JD says: "translate raw transactional data into demand-planning
insights."
WRONG: "Deliver daily SQL reports... supporting ongoing
demand-planning decisions." (Resume never says demand-planning;
adding it extrapolates beyond evidence to chase JD vocabulary.)
RIGHT: "Deliver daily SQL reports on retail sales data for the
merchandising team, supporting cross-departmental decisions."

The test: can you point to the line in RESUME or EVIDENCE that
uses this term, or a near-synonym the candidate's actual work
genuinely earns? If not, the term doesn't belong in the bullet.

**On numbers (read carefully — this is the most-failed rule):**

Quantify when an honest metric is in the source. Numbers — money, people,
time, percentage, scale, frequency, rank — convert claims to evidence.
If the resume says a model lifted conversion 12%, the bullet says 12%.

**Do NOT invent a percentage.** If the source says "improved the checkout
flow" with no number, do NOT write "improved the checkout flow by 30%."
The placeholder pattern (`[INSERT METRIC]`) governed by the section below
is the correct tool when the candidate has done a specific thing whose
only missing piece is the number — not when there's no metric to begin
with.

When no honest metric exists, substitute scope, frequency, selectivity,
or recognition:

- Scope: "across 3 sites and 450 employees", "for a 6-person team",
"supporting $14M in annual ad spend"
- Frequency: "shipped weekly", "ran 48 campaigns annually"
- Selectivity: "1 of 12 selected from a 200-person cohort"
- Recognition: "adopted by the data team for ongoing use", "presented
at the company all-hands"

A bullet with honest scope is strictly better than one with a fabricated
percentage. A bullet with neither — pure activity description, no scope
or outcome — should usually be cut.

**Weak -> strong examples.** The strong version always names a specific
verb, a specific scope, and a specific outcome (qualitative when no
honest metric is available). No claims are invented; the strong version
says only what the weak version implied.

WEAK: Worked on the CI/CD pipeline.
STRONG: Implemented CI/CD pipeline using Jenkins and Docker, reducing
build times 60% and lifting deployment frequency 40%.

WEAK: Responsible for vendor relationships.
STRONG: Renegotiated contracts with 3 office-supply vendors,
consolidating orders into one monthly shipment that simplified
tracking for a 60-person office.

WEAK: Helped onboard new hires.
STRONG: Redesigned the onboarding flow for a 12-person data team,
eliminating recurring first-week questions and freeing 3+
manager hours per new hire.

WEAK: Managed email marketing campaigns.
STRONG: Designed and executed 48 email campaigns annually to a 75K-
subscriber list, achieving a 28% open rate (industry average
21%) and driving $890K in attributed revenue.

WEAK: Posted on social media regularly.
STRONG: Planned and published daily content across three channels,
growing Instagram following from 2,000 to 5,800 in 8 months.

WEAK: Member of Leadership for Tomorrow Society.
STRONG: Selected as 1 of 275 participants nationwide for a 12-month
leadership-development program based on demonstrated
leadership potential.

WEAK: Helped with cost-saving initiatives.
STRONG: Reduced procurement costs $500K by consolidating spend across
12 vendors onto a single master contract.

**Self-check before emitting each bullet:**

1. Starts with a strong action verb (past tense, or present for current
role)? Verb is specific, not "managed/handled/worked on"?
2. Has a scope or outcome — even qualitative — or is this pure activity?
3. If there's a number, did it come from the source? (If you cannot
point to the RESUME/EVIDENCE line that contains it, delete it.)
4. One sentence, <=2 lines? No semicolons stitching two ideas?
5. Free of "Responsible for" and its cousins?

If a bullet would fail any check, do not emit it. A shorter, sharper
resume is strictly better than a longer one with weak bullets.

## Summary craft (the paragraph at the top)

The summary is the candidate's trailer for THIS job — not a generic
"about me." Most weak summaries fail by being either (a) a list of
adjectives the bullets don't back up, or (b) a verbatim restatement
of the strongest bullet.

**Length:** 2-4 sentences. If the candidate's identity and value are
already obvious from the H1 + first role's bullets, OMIT the summary
entirely. A missing summary is better than a generic one.

**Shape:**

1. Sentence 1: identity + level + domain. Example: "MS Business
Analytics candidate with 2 years of e-commerce data engineering
experience."
2. Sentence 2-3: the 1-2 strongest pieces of evidence from the resume
that map to the JD's top requirements. Concrete, with scope or
metric. Different angle than the top bullet — the summary frames,
the bullets prove.
3. Optional sentence 4: what the candidate is looking for, framed in
terms of the JD ("seeking to apply forecasting and SQL skills to
demand-planning roles").

**FORBIDDEN in the summary:**

- Generic adjectives unsupported by evidence: "hardworking", "passionate",
"results-oriented", "team player", "detail-oriented", "go-getter",
"self-starter", "highly motivated", "proven track record"
- First-person pronouns
- Vague unsubstantiated claims ("excellent communication skills")
- Verbatim repetition of a bullet from below
- Listing every skill the candidate has — pick the 2-3 that match the JD
- Meta-statements that claim the match instead of showing it ("directly
matching X's mandate", "perfect fit for", "ideally suited for",
"uniquely positioned to"). Let the evidence prove alignment — that's
what the bullets do. The cover letter is where alignment is stated
explicitly; the resume's job is to show, not tell.

A weak generic summary actively hurts the candidate by burning the most
valuable real estate on the page (the spot directly below the name) with
non-signal. A good summary or no summary at all.

## Skills section craft

The skills list is a set of CLAIMS about what the candidate knows. Every
line is a fact assertion: "this candidate has used Power BI." Apply the
same anchoring discipline as bullets — if a tool, language, framework,
or technology is not in RESUME or EVIDENCE, it does NOT belong in
skills, even if the JD asks for it.

You MAY:
- Cut tools that aren't relevant to the JD (don't list 10 languages
when the role calls for 2)
- Reorganize categories to surface the JD-relevant tools first
- Rename categories to match the JD's vocabulary if the substance
is identical ("BI Tools" vs "Visualization Tools")

You MUST NOT:
- Add a tool from the JD that the candidate's source does not list
- Substitute a JD tool for a similar source tool (source says
Snowflake; JD asks for BigQuery; do NOT add BigQuery — they are
not the same product even if they fill the same role)
- Pad the list with tools "everyone has" (Microsoft Office, etc.)
that the source didn't claim

INVALID (skills-section fabrication):
RESUME says: "Snowflake, dbt, Airflow"
JD says: "experience with cloud warehouses (Snowflake, BigQuery,
Redshift)"
WRONG: "Pipeline & Warehouse: Snowflake, BigQuery, dbt, Airflow"
(BigQuery isn't in the source — the candidate hasn't used it.)
RIGHT: "Pipeline & Warehouse: Snowflake, dbt, Airflow"
(Honest claim. The fit-assessment phase already noted any gap;
the resume isn't the place to close it.)

**Length and recency.** Detailed entries should cover roughly the last 10-15
years. For candidates with a longer history, compress anything older into a
single "Earlier roles" section at the end — one line per role, format
Expand Down