mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
Nothing in the framework checks a posting's language requirements
against what the candidate actually speaks. It is not one of the five
Scoring Dimensions in 04-job-evaluation.md, it is not checked in
/scrape's Step 3 fit assessment, and it is not a field in /rank's JSON
output - even though /apply's Step 1 already extracts a posting's
required language generically, with nowhere to report a mismatch to.
This adds a Language Gate, structured like the existing Eligibility
Gate (read the posting, classify against profile data, hard-stop on a
real mismatch), built on a new structured Languages table in CLAUDE.md
/ 01-candidate-profile.md. /setup now asks for it directly (Path C), or
infers it from a CV/LinkedIn export (Paths A/B - LinkedIn exports
already carry a self-rated Languages section).
The gate compares a posting's stated language requirements against
that table with three outcomes:
- Requires a language not declared at all -> hard FAIL, never
presented.
- Requires a higher level in a language that is declared (e.g. "fluent
English" against a declared B1/B2) -> FLAG, not an auto-reject -
scored and drafted normally, with the gap surfaced so the candidate
judges it themselves (a "fluent" bar reads very differently from a
strict employer vs. one that's flexible on it).
- Requires a language at or below the declared level -> clean PASS.
Wired through the three places that need it: /scrape (Step 3), /rank
(new language_gate/language_note fields alongside the existing
location veto - both are now persisted to seen_jobs.json, not just
used transiently to decide one run's shortlist), and /apply (Step 1's
language extraction now has somewhere to report to).
Out of scope, deliberately: this does not touch the free-form
Deal-breakers list or how it's used elsewhere (e.g. Scoring Dimension
4's relocation check) - that's a separate question this change takes
no position on.
Validated with two live-testing passes against real, unfetched
postings (not fabricated text) across 3 portals and 3 market languages
(Danish, German, Spanish/Argentina): 8/8 postings gated correctly in
the first pass, including ambiguous real-world wording ("you
communicate well in English") a rigid rule would have gotten wrong. A
second pass, run specifically to force a hard-FAIL case, found one
(a Danish posting requiring the ability to read Danish) and confirmed
it persists correctly and would be excluded from /rank's shortlist.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
88 lines
4.5 KiB
Markdown
88 lines
4.5 KiB
Markdown
# Search Queries for Job Scraper
|
|
|
|
<!-- SETUP: Customize these queries based on your skills, target roles, and location -->
|
|
|
|
## Installed portal CLIs (primary for `/scrape`)
|
|
|
|
`/scrape` discovers every portal skill under `.agents/skills/*/SKILL.md` and runs its CLI first. Shipped country-agnostic CLIs include `linkedin-search` and `freehire-search`; Danish demos and any skill you add with `/add-portal` are included the same way. You do **not** need a matching `site:` line below for those CLIs to run.
|
|
|
|
The `site:` query templates in this file are the **WebSearch fallback** — for portals without a CLI, company career pages, or when a CLI fails.
|
|
|
|
**Language scope:** write every query category in every language listed in your CLAUDE.md Languages table (typically 1-2, sometimes more). A posting requiring a language you have *not* declared, as a job condition, is excluded before scoring; a posting requiring a *higher level* than you declared in a language you *do* work in is flagged for your own judgment, not excluded — see `04-job-evaluation.md`'s Language Gate, the single source of truth for this rule. Translate each category's keywords rather than machine-translating word-for-word (e.g. "Frontend Developer" -> "Desarrollador Frontend", not a literal word-for-word translation) if you work in more than one language.
|
|
|
|
## Search Sites
|
|
|
|
Primary (your market's job boards - scaffold one with `/add-portal`):
|
|
- **[YOUR_JOB_BOARD]** - your market's largest general job board
|
|
- **linkedin.com/jobs** - LinkedIn job listings (filter: [YOUR_COUNTRY] / [YOUR_CITY]); also covered by `linkedin-search` CLI
|
|
- **[YOUR_INDUSTRY_JOB_BOARD]** - a niche/industry board for your field (optional)
|
|
- **[YOUR_ADDITIONAL_JOB_BOARD]** - another major board for your market (optional)
|
|
|
|
Secondary (company career pages via Google):
|
|
- Direct Google searches with `site:` filters for known target companies
|
|
|
|
## Query Categories
|
|
|
|
Queries are grouped by priority. Write **each category in every language from your Languages table** (see Language scope above). Combine each query with your location terms (e.g. your city, region, or metro area) where the site supports it.
|
|
|
|
### Priority 1: [YOUR_PRIMARY_ROLE_TYPE]
|
|
|
|
These match your strongest and most desired career direction.
|
|
|
|
```
|
|
site:[YOUR_JOB_BOARD] "[YOUR_PRIMARY_JOB_TITLE]" [YOUR_CITY]
|
|
site:[YOUR_JOB_BOARD] "[YOUR_KEY_SKILL]" [YOUR_CITY]
|
|
site:linkedin.com/jobs "[YOUR_PRIMARY_JOB_TITLE]" [YOUR_COUNTRY]
|
|
```
|
|
|
|
### Priority 2: [YOUR_DOMAIN_EXPERTISE]
|
|
|
|
These match your domain expertise.
|
|
|
|
```
|
|
site:[YOUR_JOB_BOARD] [YOUR_DOMAIN_KEYWORD_1] [YOUR_CITY] OR [YOUR_REGION]
|
|
site:[YOUR_JOB_BOARD] [YOUR_DOMAIN_KEYWORD_2] [YOUR_COUNTRY]
|
|
site:linkedin.com/jobs [YOUR_DOMAIN_KEYWORD_1] [YOUR_CITY] [YOUR_COUNTRY]
|
|
```
|
|
|
|
### Priority 3: [YOUR_ADJACENT_ROLE_TYPE]
|
|
|
|
Adjacent roles you could pivot into.
|
|
|
|
```
|
|
site:[YOUR_JOB_BOARD] "[YOUR_ADJACENT_TITLE_1]" [YOUR_KEY_SKILL] [YOUR_CITY]
|
|
site:[YOUR_JOB_BOARD] "[YOUR_ADJACENT_TITLE_2]" [YOUR_KEY_SKILL] [YOUR_CITY]
|
|
```
|
|
|
|
### Priority 4: Broader Technical / Consulting
|
|
|
|
Wider net for general technical roles.
|
|
|
|
```
|
|
site:[YOUR_JOB_BOARD] [YOUR_KEY_SKILL] developer [YOUR_CITY]
|
|
site:linkedin.com/jobs "[YOUR_KEY_SKILL] developer" [YOUR_CITY]
|
|
site:[YOUR_JOB_BOARD] "technical consultant" [YOUR_DOMAIN] [YOUR_CITY]
|
|
```
|
|
|
|
## Location Filter
|
|
|
|
When evaluating results, verify the job location is within reasonable commute distance from your home. Define acceptable areas:
|
|
- [YOUR_CITY] and surrounding areas
|
|
- [ACCEPTABLE_AREA_1]
|
|
- [ACCEPTABLE_AREA_2]
|
|
- [BORDERLINE_AREA] (borderline - ~X min by transit)
|
|
- [TOO_FAR_AREA] (too far)
|
|
|
|
## Language Filter
|
|
|
|
Your working languages and levels are in CLAUDE.md's Languages table. When filtering scraped results, apply `04-job-evaluation.md`'s Language Gate: a posting requiring a language you haven't declared at all is excluded; a posting requiring a higher level than you declared in a language you do work in is not excluded, flag it clearly instead (see `job-scraper/SKILL.md`'s Step 3 "Quick Fit Assessment" for how the flag surfaces in `/scrape` output). Postings simply *written* in a language you don't work in, that don't require it on the job, are fine.
|
|
|
|
## Date Filter
|
|
|
|
Only include jobs posted within the last 14 days, or with an application deadline that has not yet passed. If a posting date cannot be determined, include it but flag as "date unknown".
|
|
|
|
## Adapting Queries
|
|
|
|
If the user specifies a focus area, select queries from the matching category and also generate 2-3 custom queries for that focus. For example:
|
|
- "/scrape [focus_area]" -> relevant category queries + custom focus-specific queries
|