You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
3.6 KiB
Markdown
110 lines
3.6 KiB
Markdown
# gimme-job Learn Mode — System Instructions
|
|
|
|
You are working inside the **gimme-job** codebase.
|
|
|
|
## Your Goal
|
|
|
|
Create or update a job site adapter so that `gimme-job run --site {site_id}` works without any AI assistance at runtime.
|
|
|
|
## Core Rules
|
|
|
|
1. **Runtime must not require AI.** The adapter must work with pure Python + Playwright.
|
|
2. **Use Chrome persistent profile `JobAgent`** — never `browser.new_context()` directly.
|
|
3. **No storage_state** — Chrome profile reuse is the only session persistence mechanism.
|
|
4. **Prefer robust selectors**: `aria-label`, `data-*` attributes, semantic HTML. Avoid brittle `nth-child`.
|
|
5. **Always define fallback selectors** (list multiple per field in the manifest).
|
|
6. **Handle zero-result states explicitly** — `ZERO_RESULTS_EXPECTED` is a normal exit.
|
|
7. **No detail page visits by default** — extract from card list only.
|
|
8. **If a date filter exists in the UI, use it.**
|
|
|
|
## Files to Generate
|
|
|
|
For site_id `{site_id}`:
|
|
|
|
1. `sites/{site_id}.yaml` — Site 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
|
|
|
|
## Adapter Interface
|
|
|
|
The adapter must implement `BaseJobSiteAdapter` from `gimme_job/adapters/base.py`:
|
|
|
|
```python
|
|
class BaseJobSiteAdapter(Protocol):
|
|
site_id: str
|
|
def prepare(self, page: Page, config: SiteManifest) -> None: ...
|
|
def apply_search(self, page: Page, query: SearchQuery) -> None: ...
|
|
def apply_filters(self, page: Page, query: SearchQuery) -> None: ...
|
|
def collect_cards(self, page: Page, config: SiteManifest) -> list[RawJobCard]: ...
|
|
def paginate(self, page: Page, page_index: int, config: SiteManifest) -> bool: ...
|
|
def normalize(self, raw: RawJobCard) -> JobPostingCandidate: ...
|
|
```
|
|
|
|
Use `@register("{site_id}")` from `gimme_job/adapters/registry.py` to register the adapter.
|
|
|
|
## Manifest Schema
|
|
|
|
Key YAML fields (see `gimme_job/models/manifest.py` for full Pydantic schema):
|
|
|
|
```yaml
|
|
site_id: {site_id}
|
|
enabled: true
|
|
repair_needed: false
|
|
identity:
|
|
label: "Site Name"
|
|
base_url: "https://..."
|
|
start_url_template: "https://...?q={keywords_urlencoded}"
|
|
browser:
|
|
profile_mode: persistent_chrome_profile
|
|
profile_name: JobAgent
|
|
headed_on_learn: true
|
|
headless_on_run: true
|
|
search:
|
|
keyword_mode: url_param # url_param | input_field | none
|
|
date_mode: none # none | url_param | click_filter
|
|
date_filter_text: null
|
|
result_list_wait_selector: null
|
|
pagination:
|
|
mode: none # none | next_button | url_increment
|
|
next_button_selectors: []
|
|
max_pages: 3
|
|
extract:
|
|
container_selectors:
|
|
- "CSS selector for each job card container"
|
|
fields:
|
|
title:
|
|
text: ["h2 a", "h3"]
|
|
company:
|
|
text: [".company-name"]
|
|
location:
|
|
text: [".location"]
|
|
posted_text:
|
|
text: ["time", ".date"]
|
|
url:
|
|
attr:
|
|
selector: "a[href*='/jobs/']"
|
|
name: href
|
|
post_filters:
|
|
include_posted_text:
|
|
- "today"
|
|
- "1 day ago"
|
|
```
|
|
|
|
## Smoke Test Requirements
|
|
|
|
The smoke test in `tests/adapters/test_{site_id}.py` must:
|
|
1. Open the job search page
|
|
2. Either detect result cards OR confirm zero-result state
|
|
3. Extract at least 2 fields from a card (title + one other) OR confirm zero results
|
|
4. Pass with `pytest tests/adapters/test_{site_id}.py`
|
|
|
|
## Acceptance Criteria
|
|
|
|
Learning is successful when ALL of these are true:
|
|
- `sites/{site_id}.yaml` exists and is valid
|
|
- `gimme_job/adapters/{site_id}.py` exists with no syntax errors
|
|
- `tests/adapters/test_{site_id}.py` exists
|
|
- `gimme-job test {site_id}` passes (smoke test)
|
|
- At least 1 field extraction logic is verified
|