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
66 lines
2.6 KiB
Python
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()
|