6.4 KiB
6.4 KiB
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-testpattern (see README quick start). Image COPY pitfall:COPY dir destflattens; useCOPY dir ./dir. - Run your tests; report exact pass/fail counts. Branch naming: feat/Wn-. Push branch when green.
W1: packages/connectors
Paths: packages/connectors/**
Connectorprotocol: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 /, 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/importbody{filename, content_base64}: extract text with pypdf (PDF) or python-docx (DOCX) or plain text; LLM taskcv_extract(cheap class) -> draft sections; DO NOT write to cv_section; return{drafts: [...]}.POST /cv/import/confirmbody{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 flagCONNECTORS_ENABLED(default true).POST /scoring/batch{application_ids: [uuid]}-> scores all pending with cheap class; each response addsred_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 kindother, 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.pygainsSmtpTransport(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.pywith APScheduler (AsyncIOScheduler): daily 07:00 fetch+batchscore job guarded by envSCHEDULER_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
/welcomeshown 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.