mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
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:
4 files changed
+45
-8
No files matched your search
@@ -143,7 +143,7 @@ class SeenJobsDedupContinuityTests(unittest.TestCase):
|
||||
text = SCRAPER_SKILL.read_text(encoding="utf-8")
|
||||
self.assertRegex(
|
||||
text,
|
||||
r"URL matches any existing `seen_jobs\.json` entry, regardless of\s+that entry's key",
|
||||
r"URL matches any existing `(?:job_scraper/)?seen_jobs\.json` entry, regardless of\s+that entry's key",
|
||||
"legacy seen_jobs entries must be matched by URL during the key-rule transition",
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user