fix(scrape): use the full job_scraper/seen_jobs.json path in every step of the spec (#608)

* fix(scrape): use the full job_scraper/seen_jobs.json path in every step of the spec

Step 0 reads job_scraper/seen_jobs.json but Steps 1-4 and the rules used the bare
filename. Step 4 is where the file is written, so an agent following it literally
can create seen_jobs.json in the repo root, outside the **/job_scraper/seen_jobs.json
ignore rule, where it gets committed with the user's scrape history.

Adds a spec test that rejects bare references; relaxes the existing dedup-continuity
regex to accept the qualified path.

* docs(changelog): entry for #608

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Mads Lorentzen <madslorentzen_17@hotmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-17 20:10:20 +02:00
1 parent 822d19a2ba
commit 8123d88f30
4 files changed
+45 -8

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
"""Every seen_jobs.json reference in /scrape's spec 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
literally can create `seen_jobs.json` in the repo root. The ignore rules only
cover `**/job_scraper/seen_jobs.json`, so a root-level copy is untracked-by-rule
only by accident: it shows up in `git status`, gets swept into a commit, and
publishes the user's scrape history. Pinning the full path in the spec keeps
the read path, the write path and the ignore rule pointing at the same file.
"""
import re
import unittest
from pathlib import Path
SKILL = Path(__file__).resolve().parent.parent / ".claude" / "skills" / "job-scraper" / "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")
if __name__ == "__main__":
unittest.main()