fix(specs): qualify seen_jobs.json as job_scraper/seen_jobs.json in /rank, /notion-sync and /upskill (#617)

Follow-up to #608 at the maintainer's invitation. /scrape already names the full
path; the three readers still used the bare filename, which an agent can resolve
to the repo root (outside the **/job_scraper/seen_jobs.json ignore rule).
The guard test now covers all four specs; test_notion_sync_command pins the
qualified phrase. 04-job-evaluation.md is left alone on purpose: forks
personalize it, and a one-word fix there would force a framework_version bump.
This commit is contained in:
Mariano Gobea Alcoba authored and GitHub committed 2026-10-18 08:09:09 +02:00
1 parent 12b757189a
commit 24db6b3b59
6 files changed
+43 -25

No files matched your search

+1 -1
View File
@@ -49,7 +49,7 @@ class NotionSyncCommandSpec(unittest.TestCase):
# lookup of `role` on the entry, which never matches, so one job became
# two Notion rows with different Keys. The join is role-vs-title.
text = COMMAND.read_text(encoding="utf-8")
line = next(l for l in text.splitlines() if "matched to `seen_jobs.json` entries" in l)
line = next(l for l in text.splitlines() if "matched to `job_scraper/seen_jobs.json` entries" in l)
self.assertIn("company + title", line)
self.assertIn("`title`", line)
self.assertIn("`role`", line)
+18 -8
View File
@@ -1,4 +1,4 @@
"""Every seen_jobs.json reference in /scrape's spec names the full path.
"""Every seen_jobs.json reference in the scrape/rank/notion-sync/upskill specs names the full path.
Step 0 reads `job_scraper/seen_jobs.json`, but later steps (notably Step 4,
where the file is written) used the bare filename. An agent following Step 4
@@ -12,18 +12,28 @@ import re
import unittest
from pathlib import Path
SKILL = Path(__file__).resolve().parent.parent / ".claude" / "skills" / "job-scraper" / "SKILL.md"
ROOT = Path(__file__).resolve().parent.parent
# /scrape writes the file; the others read it. A bare name in a reader spec is the
# same trap: an agent asked to "read seen_jobs.json" looks in the repo root first.
SPECS = [
ROOT / ".claude" / "skills" / "job-scraper" / "SKILL.md",
ROOT / ".claude" / "commands" / "rank.md",
ROOT / ".claude" / "commands" / "notion-sync.md",
ROOT / ".claude" / "skills" / "upskill" / "SKILL.md",
]
BARE = re.compile(r"(?<!job_scraper/)(?<![\w/.-])seen_jobs\.json")
class ScrapeSeenJobsPathSpec(unittest.TestCase):
def test_no_bare_seen_jobs_reference(self):
offenders = [
f"line {i}: {line.strip()[:90]}"
for i, line in enumerate(SKILL.read_text(encoding="utf-8").splitlines(), 1)
if BARE.search(line)
]
self.assertEqual(offenders, [], "use job_scraper/seen_jobs.json everywhere in the spec")
for spec in SPECS:
with self.subTest(spec=spec.relative_to(ROOT).as_posix()):
offenders = [
f"line {i}: {line.strip()[:90]}"
for i, line in enumerate(spec.read_text(encoding="utf-8").splitlines(), 1)
if BARE.search(line)
]
self.assertEqual(offenders, [], "use job_scraper/seen_jobs.json everywhere in the spec")
if __name__ == "__main__":