Phase 0 — measurement gate: - tests/test_contracts.py: BOARDS↔NORMALIZERS alignment, funnel-shape guards, overlap() None contract, and the frontend↔backend EXACT-STRING vocab check (scout.ts options must resolve in coerce.py, else silent keyword-fold). - tests/run_regression.py + fixtures/regression_set.json (+ _seed_regression.py): a frozen 120-pair set (tech + non-tech) Opus-judged for recruiter-fit targets. Two metrics: score MAE and SIFT TOP-K MEMBERSHIP RECALL (do the best jobs reach Opus?). Baseline: MAE 24.7, recall 0.68 — i.e. ~32% of the genuinely-best jobs are cut before the curator ever sees them (worse for non-tech). Phase 1 — coverage (India non-tech): - Enable Indeed (misceres) + enrich indeed_to_scoutjob with details.description + location_mode (jobType is employment type, not skills → required_skills now []). - Add WorkIndia (shahidirfan) — India blue/grey-collar non-tech: build_workindia_input + workindia_to_scoutjob (real skills + description; per-job apply URL from job_id since source_url is generic and would dedup-collapse the deck). - Drop Wellfound from the stack (US-startup-heavy + 400s); kept registered-but-off. - BOARDS_ENABLED = naukri,foundit,linkedin,indeed,workindia. Pool ~90 → ~140 jobs/search. Regression scores unchanged (engine scoring untouched). 31 tests pass.
matchmaking-v2 (Scout)
Fresh, on-demand matchmaking service replacing the dead nightly aggregator. Same agent-mesh mold as the other GrowQR services (FastAPI · a2a card · /a2a/tasks · orchestrator-routed). Not a drop-in — it keeps the existing 4 skills working and adds new actions from scratch.
Layout
app/
main.py FastAPI app (card + /a2a/tasks + /api/v1/health), lifespan worker
config.py lean settings (no corpus DB, no scrape schedule)
a2a/ card.py (discovery), auth.py (bearer), tasks.py (orchestrator entry)
agent/session.py Session: on_session_start / on_user_action dispatch ← the brain
adk/worker.py Redis-Streams worker (graceful no-op without Redis)
api/v1/health.py /api/v1/health
engine/
board_adapters/ ScoutPrefs → Apify actor inputs (Naukri-first, verified maps)
... the §3 cascade (normalize→filter→utility→fusion→rerank) lands here
contracts/ Pydantic contracts (user_context, transport, …) — added per slice
research/ docs/ (ENGINE_DESIGN, SIGNAL_AUDIT_V2, ENGINE_INPUTS, …) + poc/ (auto-apply)
Contract (how it connects)
Frontend useAgentSession (page "job-matching") → orchestrator (routes by card name
= matchmaking-service) → POST /a2a/tasks {action, params, user_context} → Session pushes
agent_data{action,data} → orchestrator → frontend latestData[action].
Skills: existing get_feed · sync_preferences · record_feedback · get_opportunity_detail;
new run_search · tailor_resume · submit_application · get_apply_proof (stubbed). Each new action
needs: card skill (here) + orchestrator action-map entry + frontend sendAction wiring.
Run (local)
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8006
# card: GET http://localhost:8006/.well-known/agent-card.json
# health: GET http://localhost:8006/api/v1/health
# action: POST http://localhost:8006/a2a/tasks (Bearer dev-a2a-key)
Build order (full-stack slices)
- ✅ scaffold (this) — bootable skeleton, contract wired, handlers stubbed.
- on-demand
run_search— board_adapters → Apify → engine cascade → ranked feed (+ frontend wire). - feedback labels +
record_feedback. 3.tailor_resume. 4.submit_application+get_apply_proof. - cut over from old
:8006, decommission corpus.