Files
claude-resume-kit/tests/test_scout_matching.py
dennisthiessenandClaude Opus 5 afd8174bbd fix(F11-F13): scout selection bugs that dropped roles before any judgement
These three all cost roles BEFORE a human ever read them, which is the
expensive kind of defect: a job that never reaches the report cannot be
evaluated at all.

F11 - dedupe by requisition, not title
- The old rule collapsed everything sharing a normalised title within one
  employer. Two different reqs called "Senior Data Engineer" in Bern and Zurich
  became one row, and different teams hiring the same title vanished.
- Requisition identity is now the key, parsed from the URL with the same
  patterns the decision log uses, employer-scoped so two boards cannot collide.
- The legitimate case the old rule existed for - one role republished per
  location - is a SAME-REQUISITION case, so those merge their locations.
- A shared title across different requisitions is now a warning, never a
  deletion.

F12 - location filters
- Substring matching accepted "Bernau bei Berlin" as Bern. Place matching is
  now whole-word.
- Thun, Zimmerwald, Baar, Olten and ~25 more Swiss towns were rejected outright
  when the adapter supplied no country. Zimmerwald is the Kdo Cy site he applied
  to; Thun is where he lives.
- Aarhus, Odense and other Danish cities added alongside Copenhagen.
- A stray "Europe" no longer implies remote eligibility next to an explicit
  onsite marker or a single-country residence restriction.
- All 14 cases named in the review now resolve correctly.

F13 - decision identity
- The generic fallback key dropped the query string, so /vacancy?job=1 and
  ?job=2 were the same key and one decision silently covered both. Identifying
  query parameters are now kept; tracking noise is not.
- The READ path had no slug fallback although the WRITE path got one on
  2026-09-04. Every RUAG repost therefore came back undecided despite a recorded
  decision - which is what happened to Lead-Architekt Network- und Richtfunk on
  2026-09-10, days after 63 RUAG rule-outs were written.
- Both paths now share `lookup_decision`. Verified on a live RUAG run: 7
  reposted requisitions recovered their decisions.
- A slug match is reported as RELATED with the matched URL, never applied as if
  it were the same requisition: a repost can differ materially, as RUAG
  demonstrated when it moved a disqualifier into a job title.

tests/test_scout_matching.py added. Full suite 34/34 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qej5SBvhcbGXHwLqNWiskM
2026-09-11 19:50:17 +02:00

66 lines
2.6 KiB
Python

"""Scout selection regressions from the 2026-09-11 review (F11 onwards).
These are the cases where a bug silently removed or mis-ranked a role *before* any human
judgement happened, which is the expensive kind: a job that never reaches the report cannot
be evaluated at all.
"""
from __future__ import annotations
import sys
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "job_scout"))
import scout # noqa: E402
def process(jobs, cid="acme", display="Acme"):
results, stat, warnings = scout._process_company(
cid, display, {}, jobs, {}, "2026-09-11", {})
return results, stat, warnings
class RequisitionDedupeTests(unittest.TestCase):
"""F11: title is not identity."""
def test_two_requisitions_sharing_a_title_both_survive(self):
jobs = [
{"id": "R-1", "title": "Senior Data Engineer",
"location": "Bern, Switzerland", "url": "https://acme.example/jobs/R-1"},
{"id": "R-2", "title": "Senior Data Engineer",
"location": "Zurich, Switzerland", "url": "https://acme.example/jobs/R-2"},
]
results, _, warnings = process(jobs)
self.assertEqual(2, len(results), "a different requisition must never be dropped")
self.assertTrue(any("title(s) shared by different requisitions" in message
for _, message in warnings),
"the collision should be reported, not silently resolved")
def test_same_requisition_republished_per_location_is_merged(self):
jobs = [
{"id": "R-9", "title": "Platform Engineer | Switzerland | Remote",
"location": "Bern, Switzerland", "url": "https://acme.example/jobs/R-9"},
{"id": "R-9", "title": "Platform Engineer | Germany | Remote",
"location": "Munich, Germany", "url": "https://acme.example/jobs/R-9"},
]
results, _, _ = process(jobs)
self.assertEqual(1, len(results), "one requisition should stay one row")
self.assertIn("Bern", results[0]["location"])
def test_distinct_titles_are_unaffected(self):
jobs = [
{"id": "A", "title": "Data Engineer", "location": "Bern, Switzerland",
"url": "https://acme.example/jobs/A"},
{"id": "B", "title": "Platform Engineer", "location": "Bern, Switzerland",
"url": "https://acme.example/jobs/B"},
]
results, _, warnings = process(jobs)
self.assertEqual(2, len(results))
self.assertEqual([], [m for _, m in warnings if "title(s) shared" in m])
if __name__ == "__main__":
unittest.main()