# job-search plugin — contributor guide

This repo is the **engine** (a Claude Code plugin). It must never contain anyone's personal data: no real names,
resumes, emails, sheet ids, connector UUIDs, or real run output. Personal data lives in a user's data folder
(`JOB_SEARCH_HOME`, found by walking up from the cwd to a folder with `config.yaml` + `data/`).

## Layout
- `skills/job-search/SKILL.md` — the orchestration Claude follows; `references/` loaded on demand.
- `skills/job-search/scripts/` — stdlib + PyYAML engine. `common.py` (paths, config + pack merge, parsing),
  `jobsearch.py` (plan/feeds/filter/decide/shortlist/publish/…), `sources/` (fetchers; package), `db.py` (SQLite state +
  migrations), `tracker.py` (csv/gsheets), `firecrawl.py` (credit budget), `discover.py`, `wwr_verify.py`,
  `setup.py` (stdlib only: init/link/env/doctor).
- `templates/` — rendered into a data folder by `setup.py` (config, preferences, CLAUDE.md, agents).
- `packs/<cc>.yaml` — all country specifics. `seeds/` — discovery seed lists + blocklist.
- `examples/data-folder/` — fictional persona; its run is generated by `tests/test_pipeline.py`.

## Rules
- Country-specific strings go in packs, user-specific ones in the data folder's config/preferences — never in code.
- Keep token cost down: deterministic work belongs in scripts; agents read `data/scoring-context.md`, not config.
- Schema changes to `jobs.db`: bump `db.SCHEMA` and add a `MIGRATIONS` step. Never break old data folders
  (legacy keys like `sheet:`, `min_inr_lpa`, `accept_any_india_location` are still read).
- New network sources: descriptive User-Agent, pacing, no bot-detection bypass; undocumented endpoints are opt-in.
- Tests are offline. Before committing: `python -m unittest discover -s tests` and `python tests/pii_scan.py`.
  Coverage (needs `pip install coverage`; ~100% of the engine is covered, CI fails under 95%):
  `COVERAGE_FILE=$PWD/.coverage coverage run -m unittest discover -s tests && coverage combine && coverage report`.
  New engine code needs tests; mock the network (`sources.net.get`, `get_json`, `post_json`, `discover._get`).
- CI runs Python 3.9 and 3.12; 3.9 rejects syntax newer code allows (e.g. a backslash inside an f-string expression).
  Before every push also run the suite on 3.9 (`/usr/bin/python3 -m venv <scratch>/py39 && <scratch>/py39/bin/pip install
  PyYAML`, then `<scratch>/py39/bin/python -m unittest discover -s tests`); the newest local Python is not enough.
- Regenerate the example run after changing filter/shortlist/publish output:
  `JS_WRITE_EXAMPLE=1 python -m unittest discover -s tests -p test_pipeline.py`.
- Regression-check filter changes: run old and new `filter` on the same raw `feeds.jsonl` with an empty seen list
  in scratch data folders and diff key → reason maps.
- Data copied from other projects (seeds, lists) needs a compatible license and an entry in `THIRD_PARTY_NOTICES.md`.
