fix: expire scored jobs with passed preserved deadlines (#560)

This commit is contained in:
Rudy Mizrahi Celekli authored and GitHub committed 2026-10-15 11:39:10 +02:00
1 parent 8a3094f138
commit 889b82bf13
3 files changed
+88 -1

No files matched your search

+8
View File
@@ -29,6 +29,14 @@ per-file diff commands.
### Fixed
- **`/rank` retires scored jobs whose preserved deadline has passed**
(`tools/rank_state.py`, `tests/test_rank_state.py`) - a fresh `null` deadline
preserves the stored date, but `apply` previously wrote the job as `ranked`
even after that date had passed. The later sweep excludes re-scored keys,
so it could not correct the state or shortlist. Apply now expires the job
before changing rank metadata; current-day deadlines, explicit future
extensions, invalid dates, dry runs, and scoring errors keep their behavior.
- **`/reset profile` keeps the `framework_version` frontmatter on the two files it rewrites**
(`.claude/commands/reset.md` Step 3, `tests/test_reset_command.py`) - the full-file skeletons
for `01-candidate-profile.md` and `02-behavioral-profile.md` started at the `#` title, while
+67
View File
@@ -259,6 +259,73 @@ class Apply(RankStateCase):
path.write_text(json.dumps(payload), encoding="utf-8")
return str(path)
def test_scored_keys_keep_past_deadlines_out_of_the_shortlist(self):
for status in ("new", "ranked", "expired"):
with self.subTest(status=status):
original = entry(status=status, deadline="2026-09-02")
if status != "new":
original.update(rank_score=61, rank_date="2026-08-31", strengths=["stored strength"])
self.write_state({"a": original})
args = ["candidates", "--tracker", str(self.tmp / "none.csv")]
if status != "new":
args.append("--all")
selected = self.run_tool(*args)["selected"]
self.assertEqual([row["key"] for row in selected], ["a"])
result = {"key": "a", "status": "scored", "deadline": None,
"scores": {dimension: 80 for dimension in ("technical", "experience", "behavioral", "career")},
"location_verdict": "PASS", "language_gate": "PASS", "language": "en",
"strengths": ["fresh strength"], "gaps": []}
out = self.run_tool("apply", "--results", self.results([result]))
self.assertEqual(out["ranked"], [])
self.assertEqual(out["vetoed"], [])
self.assertEqual([row["key"] for row in out["expired"]], ["a"])
self.assertEqual(self.read_state()["a"], dict(original, status="expired"))
swept = self.run_tool("sweep", "--write", "--exclude", "a")
self.assertEqual(swept["swept"], 0)
self.assertEqual(self.read_state()["a"]["status"], "expired")
def test_deadline_apply_boundaries_and_dry_run_keep_existing_semantics(self):
cases = [
(TODAY, None, "scored", "ranked"),
("2026-09-04", None, "scored", "ranked"),
("ASAP", None, "scored", "ranked"),
(None, None, "scored", "ranked"),
("2026-09-02", "2026-09-04", "scored", "ranked"),
(None, None, "expired", "expired"),
("2026-09-02", None, "scored", "expired"),
]
for stored, fresh, status, expected in cases:
with self.subTest(stored=stored, fresh=fresh, status=status):
self.write_state({"a": entry(deadline=stored)})
before = self.state.read_bytes()
result = {"key": "a", "status": status, "deadline": fresh,
"scores": {dimension: 80 for dimension in ("technical", "experience", "behavioral", "career")}}
path = self.results([result])
dry = self.run_tool("apply", "--results", path, "--dry-run")
self.assertFalse(dry["written"])
self.assertEqual(self.state.read_bytes(), before)
self.assertEqual([row["key"] for row in dry[expected]], ["a"])
out = self.run_tool("apply", "--results", path)
self.assertEqual([row["key"] for row in out[expected]], ["a"])
self.assertEqual(self.read_state()["a"]["status"], expected)
self.assertEqual(self.read_state()["a"]["deadline"], fresh or stored)
def test_past_deadline_does_not_bypass_scoring_errors(self):
for result in ({"key": "a", "status": "scored", "scores": {"technical": 80}},
{"key": "missing", "status": "scored", "scores": {}}):
with self.subTest(result=result):
original = entry(deadline="2026-09-02")
self.write_state({"a": original})
before = self.state.read_bytes()
path = self.results([result])
dry = self.run_tool("apply", "--results", path, "--dry-run", expect=1)
self.assertEqual(len(dry["errors"]), 1)
self.assertEqual(self.state.read_bytes(), before)
out = self.run_tool("apply", "--results", path, expect=1)
self.assertEqual(len(out["errors"]), 1)
self.assertEqual(out["expired"], [])
self.assertEqual(self.read_state()["a"], original)
def test_weights_bands_and_persisted_fields(self):
self.write_state({"a": entry()})
out = self.run_tool(
+13 -1
View File
@@ -289,6 +289,19 @@ def cmd_apply(args) -> int:
errors.append({"key": key, "error": str(exc)})
continue
# A missing deadline from the fresh fetch is not a correction to the
# stored date. Scored keys are excluded from the later sweep, so apply
# must retire a passed deadline before updating any ranking metadata.
parsed = parse_iso(result.get("deadline") or entry.get("deadline"))
if parsed is not None and parsed < today:
entry["status"] = "expired"
if result.get("deadline"):
entry["deadline"] = result["deadline"]
expired.append(
{"key": key, "title": entry.get("title"), "company": entry.get("company"), "url": entry.get("url")}
)
continue
legacy = entry_location_verdict(entry)
if entry.get("location") in ("PASS", "FAIL", "FLAG"):
entry.pop("location", None) # legacy verdict, never a place
@@ -312,7 +325,6 @@ def cmd_apply(args) -> int:
if isinstance(value, list):
entry[field] = [str(b) for b in value][:3]
parsed = parse_iso(entry.get("deadline"))
rows.append(
{
"key": key,