From 9c36c578ef929fd766dabe3c842f01000c65191e Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Sun, 15 Mar 2026 09:39:06 -0700 Subject: [PATCH] feat: add Jobgether recruiter framing to cover letter generation When source == "jobgether", build_prompt() injects a recruiter context note directing the LLM to address the Jobgether recruiter using "Your client [at {company}] will appreciate..." framing rather than addressing the employer directly. generate() and task_runner both thread the is_jobgether flag through automatically. --- scripts/generate_cover_letter.py | 23 +++++++++++++++- scripts/task_runner.py | 1 + tests/test_cover_letter.py | 38 +++++++++++++++++++++++++++ tests/test_cover_letter_refinement.py | 4 ++- 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/scripts/generate_cover_letter.py b/scripts/generate_cover_letter.py index 6fe018a..e1c2c31 100644 --- a/scripts/generate_cover_letter.py +++ b/scripts/generate_cover_letter.py @@ -189,6 +189,7 @@ def build_prompt( description: str, examples: list[dict], mission_hint: str | None = None, + is_jobgether: bool = False, ) -> str: parts = [SYSTEM_CONTEXT.strip(), ""] if examples: @@ -202,6 +203,24 @@ def build_prompt( if mission_hint: parts.append(f"⭐ Mission alignment note (for Para 3): {mission_hint}\n") + if is_jobgether: + if company and company.lower() != "jobgether": + recruiter_note = ( + f"🤝 Recruiter context: This listing is posted by Jobgether on behalf of " + f"{company}. Address the cover letter to the Jobgether recruiter, not directly " + f"to the hiring company. Use framing like 'Your client at {company} will " + f"appreciate...' rather than addressing {company} directly. The role " + f"requirements are those of the actual employer." + ) + else: + recruiter_note = ( + "🤝 Recruiter context: This listing is posted by Jobgether on behalf of an " + "undisclosed employer. Address the cover letter to the Jobgether recruiter. " + "Use framing like 'Your client will appreciate...' rather than addressing " + "the company directly." + ) + parts.append(f"{recruiter_note}\n") + parts.append(f"Now write a new cover letter for:") parts.append(f" Role: {title}") parts.append(f" Company: {company}") @@ -236,6 +255,7 @@ def generate( description: str = "", previous_result: str = "", feedback: str = "", + is_jobgether: bool = False, _router=None, ) -> str: """Generate a cover letter and return it as a string. @@ -251,7 +271,8 @@ def generate( mission_hint = detect_mission_alignment(company, description) if mission_hint: print(f"[cover-letter] Mission alignment detected for {company}", file=sys.stderr) - prompt = build_prompt(title, company, description, examples, mission_hint=mission_hint) + prompt = build_prompt(title, company, description, examples, + mission_hint=mission_hint, is_jobgether=is_jobgether) if previous_result: prompt += f"\n\n---\nPrevious draft:\n{previous_result}" diff --git a/scripts/task_runner.py b/scripts/task_runner.py index 83cdc7c..f92b7b7 100644 --- a/scripts/task_runner.py +++ b/scripts/task_runner.py @@ -169,6 +169,7 @@ def _run_task(db_path: Path, task_id: int, task_type: str, job_id: int, job.get("description", ""), previous_result=p.get("previous_result", ""), feedback=p.get("feedback", ""), + is_jobgether=job.get("source") == "jobgether", ) update_cover_letter(db_path, job_id, result) diff --git a/tests/test_cover_letter.py b/tests/test_cover_letter.py index 5db4104..4903ced 100644 --- a/tests/test_cover_letter.py +++ b/tests/test_cover_letter.py @@ -115,3 +115,41 @@ def test_generate_calls_llm_router(): mock_router.complete.assert_called_once() assert "Alex Rivera" in result + + +# ── Jobgether recruiter framing tests ───────────────────────────────────────── + +def test_build_prompt_jobgether_framing_unknown_company(): + from scripts.generate_cover_letter import build_prompt + prompt = build_prompt( + title="Customer Success Manager", + company="Jobgether", + description="CSM role at an undisclosed company.", + examples=[], + is_jobgether=True, + ) + assert "Your client" in prompt + assert "jobgether" in prompt.lower() + + +def test_build_prompt_jobgether_framing_known_company(): + from scripts.generate_cover_letter import build_prompt + prompt = build_prompt( + title="Customer Success Manager", + company="Resware", + description="CSM role at Resware.", + examples=[], + is_jobgether=True, + ) + assert "Your client at Resware" in prompt + + +def test_build_prompt_no_jobgether_framing_by_default(): + from scripts.generate_cover_letter import build_prompt + prompt = build_prompt( + title="Customer Success Manager", + company="Acme Corp", + description="CSM role.", + examples=[], + ) + assert "Your client" not in prompt diff --git a/tests/test_cover_letter_refinement.py b/tests/test_cover_letter_refinement.py index 8fc5b88..852aebd 100644 --- a/tests/test_cover_letter_refinement.py +++ b/tests/test_cover_letter_refinement.py @@ -79,10 +79,12 @@ class TestTaskRunnerCoverLetterParams: """Invoke _run_task for cover_letter and return captured generate() kwargs.""" captured = {} - def mock_generate(title, company, description="", previous_result="", feedback="", _router=None): + def mock_generate(title, company, description="", previous_result="", feedback="", + is_jobgether=False, _router=None): captured.update({ "title": title, "company": company, "previous_result": previous_result, "feedback": feedback, + "is_jobgether": is_jobgether, }) return "Generated letter"