# gimme-job Project Rules for Claude Code ## Core Principles 1. **gimme-job is an "AI-assisted learning, non-AI runtime" project.** - During runtime scraping, only adapter code and manifest YAML files are used — no AI calls. - Claude Code is invoked ONLY when learning a new site or repairing a broken adapter. 2. **Runtime must never require AI.** - All adapters must work with pure Python + Playwright, reading from site manifests. - Do not add AI/LLM calls inside adapter `prepare()`, `apply_search()`, `apply_filters()`, `collect_cards()`, `paginate()`, or `normalize()`. 3. **Use Chrome persistent profile `JobAgent` for browser automation.** - Always use `playwright.chromium.launch_persistent_context()` with `user_data_dir` pointing to `workspace/chrome-profiles/JobAgent/`. - Never use `browser.new_context()` or `playwright.chromium.launch()` directly. 4. **Do not introduce `storage_state` as a new strategy.** - Chrome profile reuse is the only session persistence mechanism. 5. **Summarization uses Ollama `qwen3.5:9b` only.** - No other LLM should be called during the run pipeline. 6. **Final notification target is KakaoTalk self-memo.** - Fall back to local Markdown file only when KakaoTalk fails. 7. **When a site fails, set `repair_needed=True` — never silently ignore failures.** - After 2 consecutive failures, mark the site as `repair_needed`. - `repair_needed` sites are skipped during `run`, noted in the summary. 8. **Selector robustness rules:** - Prefer `aria-label`, `data-*` attributes, and semantic HTML over CSS class names. - Avoid brittle `nth-child` selectors unless no better option exists. - Always define fallback selectors (list multiple selectors per field). - Handle zero-result states explicitly — `ZERO_RESULTS_EXPECTED` is a normal exit. ## When Generating or Patching Adapters Each site adapter must implement the `BaseJobSiteAdapter` protocol from `gimme_job/adapters/base.py`: - `prepare(page, config)` — navigate to start URL, wait for page readiness - `apply_search(page, query)` — inject keywords/location - `apply_filters(page, query)` — apply date/type filters via UI - `collect_cards(page, config)` — extract all visible job cards as `RawJobCard` - `paginate(page, page_index, config)` — advance to next page, return `False` when done - `normalize(raw)` — clean/normalize a `RawJobCard` into `JobPostingCandidate` Each site also requires: 1. `sites/{site_id}.yaml` — manifest YAML 2. `gimme_job/adapters/{site_id}.py` — adapter Python file 3. `tests/adapters/test_{site_id}.py` — smoke test 4. `workspace/manifests/{site_id}.learning-report.md` — learning report The smoke test must verify: - The search page opens successfully - Either result cards are detected OR a zero-result state is explicitly handled - At least 2 fields can be extracted from a card (or zero-result confirmed) ## File Locations - Site manifests: `sites/{site_id}.yaml` - Adapters: `gimme_job/adapters/{site_id}.py` - Adapter tests: `tests/adapters/test_{site_id}.py` - Prompts: `gimme_job/prompts/` - Templates: `gimme_job/templates/` - Workspace artifacts: `workspace/` (captures, traces, screenshots, dom, manifests, generated) - Database: configured via `GIMME_JOB_DB_PATH` env var (default: `gimme_job.db`)