Conflict resolutions:
- backend/job/app.py: dropped duplicate pydantic import (already present below)
- backend/job/candidate/views.py: union of both sides — kept Talha's
_recommendation/_scores_by_message helpers alongside main's manual-upload
create_candidate, merged import lists and module-level config
- frontend/src/lib/apiClient.js: both sides made the same FormData fix;
kept main's version that reuses the shared multipart const
- frontend/src/screens/Candidates.jsx: kept Talha's live-data screen and
ported main's AddCandidate modal onto it — server POST via
candidatesApi.createManual with live job posts, seed-shaped buildRow
replaced by a candidates query invalidation; seed-only BulkAssign dropped
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Embeds the tested bulk-ats engine (pip install -e ., app.services.*) rather
than duplicating it. New Candidates table (auto-migrated) persists one row per
CV per job, deduped on (job_id, content_sha256) so re-scores update in place.
CandidateScoring service extracts (pypdf off-loop), despaces, scores with
bounded concurrency + cache priming via llm_setup's shared client, and
isolates per-file failures as failed rows. Routes: /candidate/score (uploads),
/candidate/score_inbox (decoded attachments), /candidate/fetch,
/candidate/fetch_by_id. Complements the agent inbox-match flow: it routes,
this scores.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>