mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
fix(upskill,notion-sync): join tracker rows to ranked entries on title, the field entries carry (#614)
Both specs said to match "on company + role" with "casefold + strip on both fields", but a seen_jobs.json entry has title and no role key (the job-scraper schema), so read literally the lookup never matched: /upskill then weighted the same job twice, once as a tracker row and once as a ranked entry - the double-count its dedupe step exists to prevent - and /notion-sync split one job into two rows with different Keys. tools/rank_state.py already does the cross-name join (tracker role against entry title); both specs now say so. Two derived tests read the entry schema and require the join wording to name title for the entry and role for the tracker; they fail on master.
This commit is contained in:
1 parent
709339758a
commit
2697fa46fb
5 files changed
+46
-3
No files matched your search
@@ -19,6 +19,7 @@ except ImportError:
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
SKILL = REPO / ".claude" / "skills" / "upskill" / "SKILL.md"
|
||||
SCRAPER = REPO / ".claude" / "skills" / "job-scraper" / "SKILL.md"
|
||||
|
||||
|
||||
def _sections(text: str) -> dict[str, str]:
|
||||
@@ -68,7 +69,7 @@ class UpskillSkillSpec(unittest.TestCase):
|
||||
def test_step3_documents_dedupe_and_gap_precedence(self):
|
||||
sections = _sections(SKILL.read_text(encoding="utf-8"))
|
||||
step3 = sections.get("Step 3: Pass 1 — Hard Skill Diff", "")
|
||||
self.assertIn("case-insensitive company + role", step3, "Step 3 must specify the dedupe key")
|
||||
self.assertIn("case-insensitive company + title", step3, "Step 3 must specify the dedupe key")
|
||||
self.assertIn(
|
||||
"/notion-sync",
|
||||
step3,
|
||||
@@ -82,6 +83,24 @@ class UpskillSkillSpec(unittest.TestCase):
|
||||
self.assertIn("(100 - fit_rating) / 100", step3)
|
||||
self.assertIn("(100 - rank_score) / 100", step3)
|
||||
|
||||
def test_step3_dedupe_names_the_field_ranked_entries_actually_carry(self):
|
||||
# The tracker has a `role` column; a seen_jobs.json entry has `title`
|
||||
# (job-scraper SKILL.md schema) and no `role` key at all. Step 3 told
|
||||
# the reader to match on "company + role" with "casefold + strip on
|
||||
# both fields", which reads as a literal lookup of `role` on the entry:
|
||||
# that never matches, so the dedupe silently failed and Step 3.2's
|
||||
# "one weight per job" counted the same job twice. The join is
|
||||
# role-vs-title, as tools/rank_state.py implements it.
|
||||
schema = SCRAPER.read_text(encoding="utf-8")
|
||||
schema_block = schema[schema.index('"<key from tools/job_key.py>"'): schema.index("```", schema.index('"<key from tools/job_key.py>"'))]
|
||||
self.assertIn('"title"', schema_block)
|
||||
self.assertNotIn('"role"', schema_block, "if entries gain a role key, revisit the join wording")
|
||||
step3 = _sections(SKILL.read_text(encoding="utf-8")).get("Step 3: Pass 1 — Hard Skill Diff", "")
|
||||
dedupe = step3[step3.index("**Dedupe.**"): step3.index("2. **Recorded gaps")]
|
||||
self.assertIn("`title`", dedupe, "the dedupe must name the entry field, title")
|
||||
self.assertIn("`role`", dedupe, "the dedupe must name the tracker column, role")
|
||||
self.assertNotIn("company + role", dedupe, "entries have no role field to match on")
|
||||
|
||||
def test_step3_handles_blank_fit_rating(self):
|
||||
"""/outcome creates tracker rows for applications made outside the
|
||||
workflow, and no rule anywhere fills fit_rating on that path - yet
|
||||
|
||||
Reference in new issue
Block a user