The decoded attachment is already the CV and the job post is already the JD, so
scoring now fires from the data we have:
- After the agent matches an inbox message, score_message_against_job runs
inline in the match task - against the assigned job if one is set, else the
top suggested job. Failures log and never fail the match.
- When a recruiter assigns a job post, the new inbox.score_message task is
queued in the background so the request never waits on OpenAI.
Idempotent by (message, job): an already-completed score is never paid for
twice. Rows are attributed to the job post owner since background tasks have
no request user. CV Import and the profile Score-with-ATS button remain as
manual fallbacks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>