diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 7690d38..ee959bb 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -114,7 +114,9 @@ "Bash(.venv/Scripts/python.exe -c \"import scout; print\\('automated:', len\\(scout.COMPANIES\\), '| manual:', len\\(scout.MANUAL_CHECK\\)\\)\")", "Bash(.venv/Scripts/python.exe scout.py)", "Bash(.venv/Scripts/python.exe scout.py --only=swissgrid)", - "Bash(git push *)" + "Bash(git push *)", + "WebFetch(domain:careers.cisco.com)", + "WebFetch(domain:job.bkw.com)" ] } } diff --git a/.claude/skills/critique/SKILL.md b/.claude/skills/critique/SKILL.md index 876e97d..945382b 100644 --- a/.claude/skills/critique/SKILL.md +++ b/.claude/skills/critique/SKILL.md @@ -21,6 +21,7 @@ If no CL .tex provided or found in session file, critique resume/CV alone (Part **Accuracy > Relevance > Impact > ATS > Brevity** Read `config.md` Provenance Flags. Verify every claim against that table. +**JD integrity:** the critique is only as valid as the JD. If the JD source is a reconstruction/inference/template rather than the real posting (see `shared_ops.md` → JD Integrity), STOP — flag it to the user and offer to scrape the live posting (Playwright recipe) before critiquing. Never score against a fabricated JD. Check `config.md` KB Corrections Log — do not flag corrected items as errors. Use the email from `config.md` Personal Info — flag if a different email appears in output. FIXED sections (from `config.md` FIXED Sections) are template-locked — do not flag for editing. Flag only VARIABLE sections. diff --git a/.claude/skills/make-resume/SKILL.md b/.claude/skills/make-resume/SKILL.md index c245116..ff89c4f 100644 --- a/.claude/skills/make-resume/SKILL.md +++ b/.claude/skills/make-resume/SKILL.md @@ -9,11 +9,14 @@ user-invocable: true Parse `$ARGUMENTS`: - File path (e.g., `JDs/*.txt`) → read that file for the JD +- A URL → this is NOT a JD, it's a pointer. Fetch the real posting text first (see JD Integrity below), save it verbatim to `JDs/_.txt`, then proceed. - Text after the path starting with "Focus:"/"Emphasize:"/"Downplay:" → focus directive - "Quick:" prefix → Quick Mode (see below) - Empty → ask the user for the JD - Inline JD text (no file path) → save to `JDs/temp_.txt`, proceed normally +**JD Integrity (read `shared_ops.md` → "JD Integrity"):** The JD must be the real posting, verbatim. NEVER invent, reconstruct, paraphrase, or infer JD content. `WebFetch` is JS-blind on careers boards — use the job_scout Playwright recipe in `shared_ops.md` to scrape JS-gated postings. If you cannot get the real text, STOP and ask the user to paste it. Do not proceed on a guessed JD. + --- ## Safety Rules (ALWAYS ENFORCED) @@ -22,6 +25,7 @@ Parse `$ARGUMENTS`: Read `config.md` Provenance Flags before generating any content. Verify every claim against that table. +- **JD is ground truth — use the real posting verbatim. NEVER reconstruct, invent, or infer a JD** (see `shared_ops.md` → JD Integrity). If you can't get the real text, STOP and ask the user to paste it. - Use the email from `config.md` Personal Info in all outputs - Resume bullets: ALL variable bullets are 2L (CV: 2L/3L mix OK, check `config.md` Document Preferences) - Source ALL bullet content from `resume_builder/experience/` files. Never fabricate. @@ -74,7 +78,7 @@ Defaults: ## Phase 0: Research & Session Setup **Read these files:** -1. The JD (from `$ARGUMENTS`) +1. The JD — the **real posting text**, verbatim. If `$ARGUMENTS` was a URL or the posting is on a JS-gated board, scrape it with the Playwright recipe in `shared_ops.md` (JD Integrity) BEFORE this step and save to `JDs/`/`output//JD_.txt`. If you cannot obtain the real text, STOP and ask the user to paste it — do NOT reconstruct or infer it. 2. `resume_builder/reference/resume_reference.md` — Budget Card, Section Specs, Char Limits, Page Budgets 3. `config.md` — Role-Type Decision Tree to identify the matching bundle diff --git a/resume_builder/reference/shared_ops.md b/resume_builder/reference/shared_ops.md index 16f1bfd..345a2ba 100644 --- a/resume_builder/reference/shared_ops.md +++ b/resume_builder/reference/shared_ops.md @@ -5,6 +5,36 @@ --- +## JD Integrity (MANDATORY — applies to every skill) + +The job description is **ground truth**. Every requirement classification, framing decision, ATS keyword, and critique is derived from it. A wrong JD silently corrupts the entire package. Therefore: + +1. **The JD must be the real posting, verbatim.** Use the exact text of the live posting. Never invent, "reconstruct," paraphrase, summarize, infer, or fill gaps from training knowledge or a JD "template." There is **no such thing as a reconstructed JD** — if you don't have the real text, you don't have a JD. +2. **A URL is not a JD.** If the user gives a link, you must fetch the actual posting text from it before doing anything else. +3. **`WebFetch` is JS-blind on careers boards** (Google, Cisco, Apple, Meta, Workday/Phenom/Greenhouse/Lever/Recruitee SPAs). It returns the static shell or a stale search cache — do NOT trust it for JD text. Use the headless-browser scraper instead (recipe below). +4. **If you cannot obtain the real JD text, STOP and ask the user to paste it.** Do not proceed to Phase 0/bullets/critique on a guessed JD. Blocking is correct; fabricating is not. +5. **Record provenance.** The session file `JD source` line must state how the JD was obtained: `pasted by user` / `live scrape via Playwright` / `file provided`. Never label a JD as authoritative unless it is the real text. + +### Fetching a JS-gated JD (Playwright recipe) + +The job_scout repo ships a Chromium + Playwright venv: `C:\Workspace\claude-resume-kit\job_scout\.venv\Scripts\python.exe` (the bare `py`/`python` on PATH do NOT have Playwright). To pull a single posting's full text: + +```bash +cd "C:/Workspace/claude-resume-kit/job_scout" && .venv/Scripts/python.exe -c " +import scout, io +b = scout._get_browser(); ctx = b.new_context(); p = ctx.new_page() +p.goto('', timeout=45000, wait_until='domcontentloaded') +p.wait_for_timeout(5000) +io.open('jd_dump.txt','w',encoding='utf-8').write(p.inner_text('body')) +scout._close_browser()" +``` + +Then Read `job_scout/jd_dump.txt`, extract the posting body (Minimum/Preferred qualifications, About, Responsibilities), and save it verbatim to `output//JD_.txt`. (Windows console can't print some Unicode — always write to a UTF-8 file, then Read it; don't `print()` the body.) See `[[reference_live_posting_check]]` in memory. + +If the scrape fails (selector/timeout/captcha), fall back to rule 4: ask the user to paste the JD. + +--- + ## Three-Session Workflow Standard JD pipeline uses 3 sessions for token efficiency + quality: