52 lines
6.4 KiB
Markdown
52 lines
6.4 KiB
Markdown
# v1.0 worker dispatch cards
|
|
|
|
Global rules for ALL workers (same as POC, still binding):
|
|
|
|
- Work only inside your assigned paths. Never touch other modules.
|
|
- Python 3.13 + uv. Node 22 + npm. NO SQLAlchemy/Alembic/Terraform. No em dashes anywhere. English code and docs.
|
|
- LLM only via packages/llm-gateway; write prompts against mock mode first, real keys optional. Budget guards stay on.
|
|
- The approval gate semantics from ADR-0001 MUST NOT be weakened. New send transports go through the same gate.
|
|
- DinD sandbox: docker compose networking between containers works; host port publishing does NOT. DB tests run via `docker compose run --rm api-test` pattern (see README quick start). Image COPY pitfall: `COPY dir dest` flattens; use `COPY dir ./dir`.
|
|
- Run your tests; report exact pass/fail counts. Branch naming: feat/Wn-<name>. Push branch when green.
|
|
|
|
## W1: packages/connectors
|
|
|
|
Paths: `packages/connectors/**`
|
|
|
|
- `Connector` protocol: `fetch(query: SearchQuery) -> list[RawPosting]`; normalizer to the job_posting shape (source, external_id, url, company, title, location, description, raw).
|
|
- `arbetsformedlingen.py`: official Platsbanken API (https://jobsearch.api.jobtechdev.se/search?q=... free, no key, GET, header accept json). Map fields (headline->title, employer.name->company, webpage_url->url, description.text->description). Params: q, region (Malmö = Skåne län filter), limit. Polite user-agent.
|
|
- `generic_url.py`: fetch a posting URL, strip boilerplate (simple readability: prefer <main>/<article>, drop nav/footer/script/style), return title/company guesses + clean description text. Must NOT follow Cloudflare-challenge sites (detect challenge pages -> raise UnsupportedSite).
|
|
- Dedupe helper: `(source, url)` + external_id keying.
|
|
- Tests: VCR-style recorded fixtures (ship JSON/HTML fixtures in tests/fixtures; NO live network in tests). Cover AF mapping, generic extractor on a messy HTML fixture, challenge-page detection, dedupe.
|
|
- README.md per package: usage + mock examples.
|
|
|
|
## W2: apps/api v1 features
|
|
|
|
Paths: `apps/api/**` (extend, do not rewrite; keep existing 47 tests green)
|
|
|
|
Migrations `002_followups.sql`: add `follow_up_after_days int` (default 7), `last_activity_at timestamptz`, `follow_up_snoozed_until date` on `application`; `interview_prep_artifact_id uuid null` on application.
|
|
|
|
New endpoints (contract addition file `docs/api-contract-v2.md` first, then implement):
|
|
- `POST /cv/import` body `{filename, content_base64}`: extract text with pypdf (PDF) or python-docx (DOCX) or plain text; LLM task `cv_extract` (cheap class) -> draft sections; DO NOT write to cv_section; return `{drafts: [...]}`. `POST /cv/import/confirm` body `{drafts}` -> creates sections. Empty/garbage file -> 422 with clear message.
|
|
- `POST /postings/fetch` `{query, region?}` -> runs AF connector (import via packages.connectors), creates discovered applications for new postings, returns counts {new, dupes}. Behind env flag `CONNECTORS_ENABLED` (default true).
|
|
- `POST /scoring/batch` `{application_ids: [uuid]}` -> scores all pending with cheap class; each response adds `red_flags: []` (extend scoring mock with red_flags field).
|
|
- `GET /today` -> `{digest: [{application_id, title, company, score}], nudges: [{application_id, days_since_sent, suggestion}], pending_approvals: n}` (SQL for nudges: state='sent' and days > follow_up_after_days and not snoozed).
|
|
- `POST /applications/{id}/interview-prep` (strong class) -> markdown Q&A stored via artifacts service as kind `other`, sets interview_prep_artifact_id. Content: 10 likely questions w/ suggested angle referencing profile sections + posting description. Mock mode returns deterministic Q&A.
|
|
- `POST /concierge/seed-demo` -> idempotent demo seed (profile 'Demo Demosson' with åäö, 6 realistic Skåne postings, varied states incl. one scored high, one sent 8 days ago for nudge demo). Returns summary counts.
|
|
- SMTP: `transport.py` gains `SmtpTransport` (env SMTP_HOST/PORT/USER/PASS/FROM, ssl on 465, starttls else). Selection order: SMTP configured -> SmtpTransport; else ClipboardTransport (marks sent + stores payload for UI copy). Gate checks UNCHANGED. Telemetry unchanged.
|
|
- Scheduler: `app/scheduler.py` with APScheduler (AsyncIOScheduler): daily 07:00 fetch+batchscore job guarded by env `SCHEDULER_ENABLED` (default false). Start/stop in app lifespan.
|
|
- Install packages into the api image: Dockerfile.test must pip install packages (COPY packages into image, `pip install -e /packages/connectors -e /packages/llm-gateway -e /packages/artifacts`).
|
|
- Tests: extend suite — CV import (mock), fetch with connector stubbed at boundary (monkeypatch connector.fetch), batch scoring ordering, /today nudges SQL correctness (backdated last_activity), interview prep artifact creation, seed idempotency (run twice, same counts), SMTP selection logic. Total api suite must exceed 60 tests, all green via `docker compose run --rm api-test`.
|
|
|
|
## W3: apps/web v1 + CI + docs
|
|
|
|
Paths: `apps/web/**`, `.forgejo/workflows/**`, `docs/**` (only ADD docs/user-guide.md + update README screenshots section placeholder; do not edit ADRs)
|
|
|
|
- Onboarding wizard at `/welcome` shown when profile.full_name empty: steps Welcome -> Import CV (upload -> drafts review -> confirm) -> "Fetch postings" (query+region form -> /postings/fetch results) -> Done -> land on /today.
|
|
- TodayView (`/` default route): digest cards + nudge cards (with "copy follow-up draft" using /today data) + pending approvals count + total cost line from /telemetry/tasks sum.
|
|
- Applications kanban: red-flag badge (⚠ with tooltip listing red_flags), nudge hint dot when in nudges, interview-prep button on detail -> fetch/generate -> show artifact content in modal, editable textarea save -> new artifact version.
|
|
- Research view: fetch form wired to /postings/fetch, scam badge column.
|
|
- Cost display component: tokens-in + tokens-out + (cost if present).
|
|
- CI (`.forgejo/workflows/ci.yml`): on push: job1 api tests via docker compose (build api-test, run), job2 package tests (uv), job3 web (npm ci + build + vitest). Use docker on runner responsibly; if actions-runner availability is uncertain, still write the YAML (server-side check happens later).
|
|
- User guide: docs/user-guide.md, from fresh-user perspective: install, first run, import, digest, approve, interview prep, costs. Plain language, short paragraphs.
|
|
- Keep all existing vitest tests green; add: wizard routing test, nudge badge render test, red flag tooltip fixture test. npm build + test must pass.
|