Un Claude Skill qui prend les scorecards d’entretien d’un candidat refusé (et, quand disponibles, les transcripts BrightHire ou Metaview), rédige un email de rejet ancré dans les preuves ou des notes de talking-points pour l’appel du recruteur, et produit les notes côté recruteur pour l’appel. Remplace le rejet-formulaire qui nuit à l’expérience candidat par un feedback personnalisé que le candidat peut réellement utiliser — et refuse de rédiger quand le rubrique est absent, quand le process n’a pas convergé, ou quand le cas est signalé par la juridiction.
Quand l’utiliser
Le candidat a atteint au moins un onsite ou une boucle en phase finale, où selon les coûts du funnel de recrutement le candidat a investi suffisamment de temps pour mériter une vraie réponse.
L’équipe dispose d’au moins deux scorecards validés sur le candidat (Ashbysubmitted: true, Greenhousestatus: complete, Leverstate: completed). Un scorecard est la vue d’un seul intervieweur ; le skill refuse de synthétiser un feedback depuis une seule perspective parce que cela expose le cabinet à des prétentions de preuve sélective.
Un rubrique de poste existe dans rubrics/<role_id>.yaml avec des ancres comportementales par dimension (la même source que lit le skill de debrief d’entretien). Le skill score contre les ancres du rubrique, pas contre la prose libre des scorecards.
Le candidat a explicitement demandé du feedback (capturé par écrit dans l’ATS), OU la juridiction de résidence du candidat est l’une où des spécificités non sollicitées n’entraînent aucun risque documenté selon les conseils RH-légal de l’utilisateur.
Un recruteur revoit et édite chaque brouillon avant envoi. Le skill écrit les brouillons sur disque et s’arrête ; il ne définit aucune action send.
Quand NE PAS l’utiliser
Envoi automatique sans revue du recruteur. Le feedback de rejet rédigé par IA et envoyé automatiquement est la façon la plus sûre de produire un incident EEOC, ADA ou de droit du travail d’état. Le recruteur est le filtre. Si votre objectif est de retirer l’humain de la boucle, c’est le mauvais workflow.
Candidats qui n’ont pas demandé de feedback dans les juridictions de refus. France (risque Code du travail sur les motifs de rejet documentés), Allemagne (déplacement de la charge de la preuve §22 AGG), et toute juridiction que le conseil RH de l’utilisateur a marquée unsolicited_feedback: deny dans le fichier de politique. Le skill refuse les spécificités dans ces cas et écrit le template de refus générique à la place. N’éditez pas le fichier de politique pour faire passer un cas de juridiction de refus.
Cas signalés par le juridique. Litige actif, demande d’accommodation non traitée, ou une plainte au dossier. Le skill renvoie un brouillon de refus générique et fait remonter le flag au recruteur. Les spécificités dans un cas signalé deviennent des preuves dans le litige.
Rejets aux étapes antérieures (screen CV, screen recruteur). Le refus par template est le bon outil là ; le coût modèle par candidat et le temps de revue du recruteur ne se rentabilisent pas à l’échelle du haut du funnel. Le skill est pour les candidats ayant atteint au moins un onsite.
Classement comparatif (vous étiez notre deuxième choix, nous avions des candidats plus forts). Le skill refusera de rédiger cela — le mapping rubrique-vers-feedback ne contient pas ce libellé et la liste de phrases interdites le grep. Le classement comparatif est ce qui transforme un rejet constructif en post Glassdoor.
Demandes d’amélioration du process (demander au candidat son feedback sur l’entretien, une recommandation ou un témoignage). Les demandes inverses dans un email de rejet sont un risque de témoignage EEOC et un préjudice pour l’expérience candidat. La liste de blocage les attrape.
Setup
Déposez le bundle. Placez apps/web/public/artifacts/rejection-feedback-claude-skill/SKILL.md dans votre répertoire de skills Claude Code (ou Skills personnalisés claude.ai, avec autorisation Tier-A pour les données candidats selon la politique IA).
Configurez la source du rubrique. Le skill lit les rubriques de poste depuis rubrics/<role_id>.yaml — même chemin utilisé par le skill de debrief d’entretien. Si le rubrique n’existe pas, le skill refuse d’exécuter. Les entretiens structurés sont le prérequis, pas ce skill.
Remplissez le mapping rubrique-vers-feedback. Copiez references/1-rubric-to-feedback-mapping.md et remplacez le libellé template par le langage orienté candidat approuvé par votre équipe par dimension de rubrique. Obtenez la validation du conseil RH une fois ; le journal d’audit capture le SHA-256 du mapping par run, donc les révisions sont visibles rétrospectivement.
Rédigez le fichier de politique de juridiction. Un fichier YAML avec un bloc par juridiction dans laquelle votre cabinet recrute. Chaque bloc définit unsolicited_feedback: allow ou deny et référence le mémo de conseil RH pertinent. Le bundle inclut un template ; les refus par défaut sont la France, l’Allemagne et toute juridiction avec des conseils de droit du travail actifs contre les motifs de rejet documentés.
Configurez l’API de l’ATS. Token API Ashby, Greenhouse ou Lever avec portée lecture sur les scorecards et les candidats. Le skill tire les scorecards par candidate_id ; il n’accepte pas de texte de scorecard collé parce que le texte collé ne peut pas être audité jusqu’à l’intervieweur source.
Optionnel : configurez le bundle de transcript. Accès API BrightHire ou Metaview. Quand un transcript_id est fourni, le skill cross-référence les affirmations des scorecards contre les tours de transcript à l’étape 4.
Testez sur un candidat clôturé. Exécutez sur un candidat déjà refusé le trimestre dernier. Comparez le brouillon du skill à ce que le recruteur a réellement envoyé. Ajustez le mapping rubrique-vers-feedback si le calibrage dérive — le mapping, pas le modèle, est généralement le levier.
Ce que le skill fait réellement
Six étapes, dans l’ordre. L’ordre compte : le filtrage par juridiction et la validation des scorecards se produisent avant que le LLM ne lise jamais le contenu du candidat, parce que lâcher le modèle sur le texte des scorecards dans un cas de juridiction de refus laisse une entrée de journal d’appel modèle avec des données identifiant le candidat que le cabinet n’avait pas besoin de retenir.
Valider la politique de juridiction et le consentement. Rechercher la juridiction du candidat dans le fichier de politique. Si la politique est unsolicited_feedback: deny et que le candidat n’a pas demandé de feedback par écrit, arrêter les spécificités et basculer vers le template de refus générique. Le choix de filtrer sur le consentement avant de tirer les scorecards maintient la story de minimisation des données propre pour RGPD Art. 5(1)(c).
Tirer les scorecards (et le transcript optionnel). Fetch via l’API ATS. Supprimer les brouillons. Si le process a moins de deux scorecards validés, arrêter — le feedback synthétisé depuis la vue d’un seul intervieweur est une opinion, pas un feedback, et expose le cabinet à des prétentions de preuve sélective.
Identifier les dimensions et les preuves. Calculer la moyenne et l’écart-type inter-intervieweurs par dimension du rubrique. Faire remonter les dimensions où la moyenne ≥ 4 (force, ouverture chaleureuse) et moyenne ≤ 2 (gap candidat). Refuser de faire remonter toute dimension avec un écart-type inter-intervieweurs ≥ 1,5 — le process n’a pas convergé, et le feedback sur une dimension non convergée ne survivrait pas à un défi « mais l’intervieweur X m’a donné 5 ». Pour chaque dimension remontée, tirer des citations de preuves verbatim depuis les scorecards (ou le transcript, quand disponible). Pas de chaîne verbatim → la dimension n’est pas remontée.
Rédiger contre le mapping rubrique-vers-feedback. Traduire au plus une force et un gap en langage orienté candidat en utilisant references/1-rubric-to-feedback-mapping.md. Plafonner à un chacun afin que le brouillon ne se lise pas comme une liste défensive. Les slots de substitution du mapping sont remplis depuis des champs structurés (scorecard, ancre de rubrique) ou la liste de sujets approuvés — le LLM ne saisit jamais librement une valeur de substitution, ce qui est le garde contre les spécificités fausses.
Filtrage des biais et des fausses spécificités. Grep du brouillon contre references/2-banned-phrase-blocklist.md. Tout résultat arrête l’exécution avec la chaîne offensante remontée. Vérifier que chaque affirmation spécifique trace vers une chaîne de preuve verbatim de l’étape 3 — les affirmations sans source arrêtent. C’est une passe séparée de l’étape 4 par conception ; la passe de filtrage ne voit que le texte du brouillon, sans connaissance des scorecards sous-jacents, donc elle ne peut pas rationaliser une phrase interdite comme « mais l’intervieweur voulait dire X ».
Écrire sur disque et journal d’audit. Écrire drafts/<candidate-id>.md et (pour route: call) drafts/<candidate-id>-call-notes.md selon le format dans references/3-output-format.md. Ajouter une ligne JSONL à audit/<YYYY-MM>.jsonl avec candidate_id_hash (SHA-256, pas l’ID brut), rubric_sha256, blocklist_sha256, mapping_sha256, dimensions remontées, résultats de la liste de blocage, ID modèle, horodatage. Pas de texte libre identifiant le candidat dans la ligne d’audit.
Le format email littéral, le fallback de refus générique et le template de notes d’appel se trouvent dans references/3-output-format.md. Le format est fixe parce que les consommateurs en aval — recruteur, candidat et tout futur réviseur d’audit — ont besoin d’un langage prévisible sans dérive propre au recruteur.
Coûts réels
Par brouillon de rejet, sur Claude Sonnet 4.5 :
Tokens LLM — typiquement 12-25 000 tokens d’input (YAML du rubrique + scorecards + instructions du skill + fichiers de référence) et 0,5-1 500 tokens d’output (le brouillon plus les notes d’appel). Sur Sonnet 4.5 c’est environ 5-10 centimes par brouillon. Une équipe recrutement exécutant 200 brouillons de rejet par mois dépense 10-20 $ en coût modèle.
Coût API ATS — zéro sur Ashby (API gratuite), Greenhouse (inclus dans le tier), Lever (inclus). Les fetchs de transcript contre BrightHire ou Metaview comptent contre le plan par siège ; les fetchs de feedback de rejet sont en lecture seule et ne consomment pas de nouveaux crédits de transcript.
Temps du recruteur — le gain est là. La rédaction manuelle d’un email de rejet structuré et ancré dans les preuves depuis les scorecards est de 20-30 minutes par candidat quand le recruteur le fait bien, ou 3 minutes quand il colle un formulaire (ce que la plupart des équipes finissent par faire à l’échelle). Le skill produit le brouillon de 20 minutes en moins de 30 secondes ; le recruteur revoit et édite en 4-7 minutes. Économie nette d’environ 15-20 minutes par rejet au niveau de qualité de brouillon réfléchi — disons 50-60 heures par mois sur une équipe exécutant 200 rejets.
Temps de setup — 30 minutes pour le mapping rubrique-vers-feedback et la politique de juridiction si votre équipe dispose déjà d’un libellé orienté candidat approuvé quelque part ; plus long si le conseil RH ne s’est pas encore penché sur le langage de feedback de rejet (auquel cas cette conversation est le prérequis, pas ce skill).
Le retour sur investissement compoundé de l’expérience candidat. Les candidats refusés avec un feedback spécifique et ancré dans les preuves sont plus susceptibles de re-candidater, de recommander d’autres, et substantiellement moins susceptibles de laisser des avis Glassdoor négatifs — des affirmations communément citées dans la littérature du recrutement dans la fourchette de 30-50 % d’intention de re-candidature, bien que nous n’ayons pas de source primaire pour ces chiffres et les traitons comme directionnels. Le retour compoundé se manifeste dans la densité du pipeline un an plus tard, pas dans le mois où le brouillon a été envoyé.
Métrique de succès
Suivez trois chiffres par mois, dans l’ATS :
Distance d’édition du recruteur par brouillon. Le nombre de caractères que le recruteur change entre le brouillon du skill et le message envoyé. Si la distance d’édition tend vers zéro, le recruteur approuve sans regard — faites remonter cela en rétro et revisitez le mapping rubrique-vers-feedback. Si la distance d’édition est constamment élevée, le mapping est mal calibré.
Taux de réponse des candidats au rejet. Les réponses à un email de rejet sont généralement des notes de remerciement-et-future-candidature (bon signal) ou des notes d’escalade (mauvais signal). Suivez le taux d’escalade comme pourcentage des rejets envoyés. Une équipe de référence exécutant des formulaires génériques voit typiquement moins de 1 % d’escalade ; l’objectif avec ce skill est de rester à ou en dessous de cette référence, pas au-dessus. Si le taux d’escalade monte, le mapping rubrique-vers-feedback produit un langage qui atterrit mal — réajustez.
Taux de re-candidature dans les 12 mois. Candidats refusés via ce skill versus candidats refusés via l’ancien formulaire générique, mesurés sur les 12 prochains mois. Le bénéfice compoundé se manifeste ici, pas dans la dépense modèle ni même dans le fil de rejet lui-même.
Comparaison avec les alternatives
Versus les templates de rejet intégrés d’Ashby. Ashby (et Greenhouse, Lever) proposent des templates de rejet avec des champs de fusion pour le nom du candidat et le poste. Ce sont des templates, pas du feedback — les champs de fusion ne tirent pas les preuves des scorecards et il n’y a pas de couche de langage ancrée dans le rubrique. Utilisez les templates Ashby pour les rejets en haut du funnel où le template est honnête. Utilisez ce skill pour les rejets en phase tardive où le template se lit comme un rejet de l’investissement de temps du candidat.
Versus les emails de refus génériques. Le refus générique est la bonne réponse dans les cas de juridiction de refus, quand le consentement n’a pas été donné, et quand le rubrique n’a pas fait remonter de spécificité défendable. Le skill écrit le template de refus générique octet-pour-octet dans ces cas. La différence est que le skill fait le choix déterministement selon la politique de juridiction et la sortie du rubrique, plutôt que le recruteur se repliant sur le générique par fatigue.
Versus notes rédigées manuellement par le recruteur. Les notes manuelles sont la référence pour les candidats seniors ou recommandés par VIP où le recruteur a le contexte relationnel et le temps. Le skill gagne sa place sur le volume — les 80 % de rejets en phase tardive où le recruteur collerait autrement un formulaire parce que la rédaction manuelle à l’échelle ne s’intègre pas à la journée. Pour le niveau senior, le fichier de notes d’appel donne au recruteur un point de départ structuré pour l’appel, et le recruteur improvise depuis là.
Versus un LLM sans fichier rubrique ni liste de blocage. C’est le mode d’échec contre lequel le skill est construit. Un LLM rédigeant depuis les scorecards seuls, sans ancrage au rubrique, sans liste de phrases interdites et sans journal d’audit, produit rapidement du texte de rejet confiant, plausible — et environ un brouillon sur vingt contiendra une citation hallucinnée, un classement comparatif ou un proxy de classe protégée. Les fichiers de checklist du bundle sont ce qui ramène le taux d’échec à quasi-zéro.
Points de vigilance
Langage impliquant l’EEOC. Gardé par la liste de phrases interdites dans references/2-banned-phrase-blocklist.md, qui s’exécute comme passe séparée à l’étape 5 sans connaissance des scorecards sous-jacents. Les résultats arrêtent l’exécution avec la chaîne offensante remontée. N’éditez pas la liste de blocage pour faire passer un brouillon — corrigez le rubrique ou le langage des scorecards à la place.
Fausses spécificités du LLM. Gardé par la règle « pas de synthèse sans citation verbatim » à l’étape 3. Chaque affirmation dans le brouillon doit tracer vers une chaîne verbatim d’un scorecard validé ou d’un transcript. Pas de chaîne verbatim → la dimension n’est pas remontée. C’est le garde contre le mode d’échec le plus courant du feedback rédigé par LLM — des citations plausibles qu’aucun intervieweur n’a réellement écrites, citées au candidat comme faits.
Langage de classement comparatif. Gardé par le mapping rubrique-vers-feedback dans references/1-rubric-to-feedback-mapping.md, qui ne contient pas de formulations comparatives, et par la liste de blocage à l’étape 5 qui le détecte s’il glisse. Le classement comparatif est ce qui transforme un rejet constructif en post Glassdoor.
Risque de preuve sélective. Gardé par l’étape 2 (arrêt si le process a moins de deux scorecards validés) et l’étape 3 (refus de faire remonter les dimensions avec un écart-type inter-intervieweurs à ou au-dessus de 1,5). Le désaccord des intervieweurs ne devient pas du feedback candidat.
Dérive vers l’envoi automatique. Gardé par l’absence de toute action send dans le skill. Les brouillons sont écrits dans drafts/<candidate-id>.md pour que le recruteur examine, édite et envoie depuis la boîte d’envoi ATS. Le recruteur est le filtre.
Préjudice du boilerplate générique. Gardé par le refus de l’étape 3 de faire remonter une dimension sans preuves verbatim — quand le rubrique ne fait rien remonter de sûr à partager, le skill écrit le template de refus générique plutôt que de synthétiser de faibles spécificités. Le refus générique est honnête ; les faibles spécificités sont pires qu’aucune spécificité.
PII dans le journal d’audit. Gardé par l’étape 6 n’écrivant que candidate_id_hash (SHA-256), jamais l’ID brut du candidat, le nom ou le texte du scorecard. Le journal d’audit est pour la reproductibilité des runs, pas pour la rétention des données candidats. Les brouillons orientés candidat vivent dans drafts/ sous la politique de rétention propre au recruteur.
Dérive de calibrage entre postes et seniorités. Gardé par des YAML de rubrique par poste et par le mapping rubrique-vers-feedback versionné par équipe. Les rejets de direction senior ont besoin d’un cadrage différent des rejets de débutant ; le fichier de mapping est là où cela vit, pas le code du skill.
Vie privée et résidence des données. Vérifiez que le skill opère dans le cadre de l’IA enterprise Tier A selon la politique IA. Le contenu des entretiens est sensible ; le candidat n’a pas consenti à ce qu’il soit traité par un modèle tiers sauf si votre politique IA et le langage de consentement de votre collecte de scorecards le couvrent explicitement.
Stack
Le bundle du skill se trouve dans apps/web/public/artifacts/rejection-feedback-claude-skill/ et contient :
SKILL.md — la définition du skill
references/1-rubric-to-feedback-mapping.md — à remplir par équipe, libellé approuvé par le conseil RH par dimension de rubrique
references/2-banned-phrase-blocklist.md — vérifications pré-vol sur le brouillon (ne pas éditer pour faire passer des brouillons biaisés)
references/3-output-format.md — les formats littéraux de l’email, du refus générique et des notes d’appel
Outils supposés que vous utilisez déjà : Claude (le modèle), Ashby ou Greenhouse ou Lever (l’ATS dans lequel vivent les scorecards), et optionnellement BrightHire ou Metaview (transcripts d’entretien pour un ancrage de preuves plus riche). Workflow frère qui partage la source du rubrique : le skill de debrief d’entretien.
---
name: rejection-feedback
description: Take a rejected candidate's interview scorecards and (where available) transcripts, draft an evidence-grounded rejection email or recruiter-call talking points, and produce the recruiter-side notes for the call. Always stops at a recruiter-review gate; never sends. Refuses to draft when the rubric is missing or the case is jurisdiction-flagged.
---
# Rejection feedback
## When to invoke
Use this skill when a recruiter needs to send personalized post-interview feedback to a candidate who reached at least an onsite or final-stage loop, and the team has structured scorecards plus a role rubric on file. Take the candidate's scorecards (across all interviewers), the role rubric, the recruiter-relationship context (was feedback explicitly offered? requested?), and the candidate's residency jurisdiction as input. Produce a Markdown rejection email draft, optional recruiter-call talking-point notes, and a one-line routing recommendation.
Do NOT invoke this skill for:
- **Auto-sending without recruiter review.** The skill writes drafts to disk and stops. There is no `send` action defined anywhere in this skill. Auto-sent rejection feedback is the single most reliable way to produce an inappropriate-content incident under EEOC, ADA, or state employment law. The recruiter is the gate.
- **Candidates who have not requested feedback in jurisdictions where unsolicited feedback creates risk.** Specifically: France (Code du travail risk on documented rejection reasons), Germany (AGG §22 evidentiary shift), and any jurisdiction where the recruiter's HR-counsel guidance disallows unsolicited specifics. The skill reads the `jurisdiction_policy.yaml` file and refuses to draft specifics for any jurisdiction marked `unsolicited_feedback: deny`.
- **EEOC-implicating language or protected-class proxies.** "Cultural fit", age inferences from graduation year, family-status references, national-origin references, accent commentary, gendered descriptors ("aggressive", "abrasive", "soft"), pregnancy-status references, disability or accommodation references. The banned-phrase blocklist in `references/2-banned-phrase-blocklist.md` runs as the final check before the draft is written. Any hit halts the run with the offending string surfaced.
- **Cases legal has flagged.** If the candidate file has a flag for active dispute, accommodation request unaddressed, or a complaint on record, the skill returns "decline to provide specific feedback — legal flag present" and writes a generic-decline draft instead.
- **Rejections from earlier stages** (resume screen, recruiter screen). Templated decline is the right tool there. This skill is for candidates who invested significant time and earned a real answer, per the [recruiting funnel](/en/learn/recruiting-funnel-metrics/) cost.
## Inputs
- Required: `candidate_id` — the ATS record ID ([Ashby](/en/tools/ashby/), [Greenhouse](/en/tools/greenhouse/), or [Lever](/en/tools/lever/)). The skill pulls scorecards via the ATS API; it does not accept pasted scorecard text, because pasted text cannot be audited back to the source interviewer.
- Required: `role_id` — used to load the role's rubric from `rubrics/<role_id>.yaml` (same source the [interview debrief skill](/en/workflows/interview-debrief-summary-skill/) reads). Without a rubric the skill refuses to run; ungrounded feedback is how false specifics get drafted.
- Required: `jurisdiction` — ISO 3166 country code for the candidate's residency at time of application. Drives which jurisdiction-policy block applies.
- Required: `feedback_requested` — boolean. `true` only if the candidate explicitly asked for feedback (in writing, captured in the ATS). `false` defaults to a generic-decline draft in jurisdictions where the policy file flags unsolicited specifics as risk.
- Optional: `transcript_id` — pointer to a [BrightHire](/en/tools/brighthire/) or [Metaview](/en/tools/metaview/) transcript bundle for the loop. When present, the skill cross-references scorecard claims against transcript evidence; when absent, the skill works from scorecards alone and labels the draft accordingly.
- Optional: `route` — one of `email`, `call`, `auto`. `auto` (default) picks based on stage reached and seniority per the routing rules in `references/3-output-format.md`.
## Reference files
Always read the following from `references/` before drafting. Without them the draft is generic, ungrounded, and risks tripping a banned phrase.
- `references/1-rubric-to-feedback-mapping.md` — the mapping from rubric dimensions to safely-sharable, candidate-facing feedback language. Replace the template placeholders with your team's approved phrasing before first use.
- `references/2-banned-phrase-blocklist.md` — the blocklist the skill greps the draft against in step 5. Patterns include EEOC-implicating terms, protected-class proxies, comparative-ranking language, and unverifiable specifics. Do not edit this file to make a draft pass.
- `references/3-output-format.md` — the literal email and call-notes format, including the routing rules.
## Method
Run these six steps in order. Steps 1-3 are deterministic gating; steps 4-5 use the LLM for synthesis and screening; step 6 is the audit log. The order matters — letting the LLM draft against unchecked scorecards produces fast, confident, EEOC-implicating output.
### 1. Validate jurisdiction policy and consent
Open `references/jurisdiction-policy.yaml` (user-supplied; template shipped in the bundle). Look up the candidate's `jurisdiction`. If `unsolicited_feedback: deny` and `feedback_requested: false`, halt specifics and switch to the generic-decline template at the top of `references/3-output-format.md`. Log the reason in the audit line.
The choice to gate on consent before pulling scorecards is deliberate: specifics drafted and then discarded still leave a model-call log entry with candidate-identifying scorecard text. Gating up front keeps the data-minimization story clean for GDPR Art. 5(1)(c).
### 2. Pull scorecards and (optional) transcript
Fetch all scorecards for `candidate_id` via the ATS API. Validate that every scorecard is signed-off (Ashby `submitted: true`, Greenhouse `status: complete`, Lever `state: completed`). Drop drafts. If the loop has fewer than two completed scorecards, halt — feedback synthesized from one interviewer's view is not feedback, it is an opinion, and exposes the firm to selective-evidence claims.
When `transcript_id` is provided, fetch the transcript bundle. The skill will cite scorecard claims against transcript turns in step 4.
### 3. Identify dimensions and evidence
For each rubric dimension, compute the cross-interviewer mean score and the standard deviation. Flag dimensions where:
- mean ≥ 4 (candidate strength, surface as the warm opening)
- mean ≤ 2 (candidate gap, candidate for feedback if safe)
- standard deviation ≥ 1.5 (interviewer disagreement — do NOT cite this dimension; the loop did not converge and the feedback would not survive a "but interviewer X scored me 5" challenge)
For each surfaced dimension, pull the verbatim evidence quotes from the scorecards (or transcript, when available). Every claim in the final draft must cite a verbatim string from the evidence pool. No verbatim string → the dimension is not surfaced.
The "no synthesis without verbatim citation" rule is the guard against false specifics. LLMs drafting feedback from scorecards will, without this rule, invent quotes that sound plausible — "the candidate struggled with system-design tradeoffs" — that no interviewer ever wrote. False specifics cited back to the candidate are how rejection-feedback workflows generate complaint emails.
### 4. Draft against the rubric-to-feedback mapping
Translate at most one strength and one gap into candidate-facing language using `references/1-rubric-to-feedback-mapping.md`. Cap at one of each so the draft does not read as a defensive list. Comparative ranking ("we had stronger candidates", "you were our second choice") is forbidden — the mapping file does not contain the language and step 5 greps it out.
For `route: call`, also draft recruiter-side talking points: bullet-point observations, the suggested phrasing for the gap, and two to three pre-prepared responses to likely candidate questions ("Was there anything I could have done differently?", "Will you keep me in mind for future roles?", "Can I get a second look?").
### 5. Bias and false-specifics screening
Grep the draft against `references/2-banned-phrase-blocklist.md`. Any hit halts the run with the offending string surfaced. Then verify that every specific claim in the draft maps back to a verbatim evidence string from step 3 — if a claim has no source, halt.
This is a separate pass from step 4 by design. The screening pass sees only the draft text, with no awareness of the underlying scorecards, so it cannot rationalize a banned phrase as "but the interviewer meant X".
### 6. Write to disk and audit log
Write the draft to `drafts/<candidate-id>.md` per the format in `references/3-output-format.md`. Write the call notes (if applicable) to `drafts/<candidate-id>-call-notes.md`. Append one JSONL line to `audit/<YYYY-MM>.jsonl` containing: `run_id`, `candidate_id_hash` (SHA-256, not raw ID), `role_id`, `jurisdiction`, `feedback_requested`, `route`, `rubric_sha256`, `dimensions_surfaced`, `blocklist_hits` (zero on success), `model_id`, `timestamp`. No candidate-identifying free text in this line.
Surface the path to the recruiter and exit. The recruiter reviews, edits, and sends from the ATS or their own outbox.
## Output format
Literal example of the email draft the skill writes to `drafts/<candidate-id>.md` for a candidate who reached an onsite for a Senior Backend Engineer role and explicitly requested feedback:
```markdown
Subject: Update on your Senior Backend Engineer interview at Acme
Hi Jamie,
Thank you for the time you invested in our interview process — the
take-home, the system-design loop, and the conversations with the
team. We appreciated the care you put into each stage.
After the team's debrief, we have decided not to move forward with
your candidacy for this role.
You asked for feedback, so here is what stood out from the loop:
- **What went well.** Your take-home submission was clear, well-tested,
and included a thoughtful note on the failure-mode tradeoffs. Two
interviewers cited the test coverage specifically.
- **Where the team landed differently.** In the system-design round,
the discussion of consistency-vs-availability tradeoffs at the
database layer did not surface the read-replica option that the
role frequently requires reasoning about. This was the dimension
that drove the team's decision.
This feedback is specific to the loop you ran with us; it is not a
ranking against other candidates and it is not a comment on your
overall engineering ability.
If a future role at Acme matches your background, we would welcome
your application.
Best,
{Recruiter name}
```
Literal example of the recruiter call-notes file written to `drafts/<candidate-id>-call-notes.md`:
```markdown
# Call notes — Jamie L. (Senior Backend Engineer)
## Frame
- Open with thanks for the time invested.
- Lead with the take-home strength (specific: test coverage note).
- Single gap: system-design read-replica reasoning. One sentence,
no piling on.
## Suggested phrasing for the gap
"In the system-design conversation, the team was looking for the
read-replica option as part of the consistency-availability tradeoff,
and that did not come up. That was the dimension that drove the
decision for this specific role."
## Likely candidate questions
Q: "Was there anything I could have done differently?"
A: Acknowledge the question. Refer back to the single gap. Do NOT
add new feedback dimensions on the call — anything not in the
written draft is off-script and creates inconsistency risk.
Q: "Will you keep me in mind for future roles?"
A: Yes if true; specifics on what kind of role. Do NOT promise a
timeline.
Q: "Can I get a second-look interview?"
A: No. The decision is final. The recruiter reiterates appreciation
and closes.
## Off-script
If the candidate raises a discrimination concern, comparative-ranking
question, or accommodation issue, the recruiter says "let me come
back to you on that" and routes to HR / counsel. The recruiter does
NOT improvise an answer.
```
Literal example of the routing recommendation appended to the draft file:
```markdown
---
Routing: call (stage: onsite, seniority: senior, prior referrer: yes)
Recruiter review required before send.
```
## Watch-outs
- **EEOC-implicating language.** *Guard:* the banned-phrase blocklist in `references/2-banned-phrase-blocklist.md` runs as a separate pass in step 5, with no awareness of the underlying scorecards, so it cannot rationalize a hit. Any hit halts the run with the offending string surfaced. Do not edit the blocklist to make a draft pass — fix the rubric or the scorecard language instead.
- **False specifics from the LLM.** *Guard:* the "no synthesis without verbatim citation" rule in step 3. Every claim in the draft must trace to a verbatim string from a signed-off scorecard or transcript. No verbatim string → the dimension is not surfaced. This is the guard against the most common failure mode of LLM-drafted feedback — plausible-sounding quotes that no interviewer actually wrote.
- **Comparative ranking language.** *Guard:* the rubric-to-feedback mapping in `references/1-rubric-to-feedback-mapping.md` does not contain comparative phrasing ("stronger candidates", "second choice"), and the blocklist in step 5 catches it if it slips in. Comparative ranking is what turns a constructive rejection into a Glassdoor post.
- **Selective-evidence risk.** *Guard:* step 2 halts if the loop has under two signed-off scorecards. Step 3 refuses to surface dimensions with cross-interviewer standard deviation at or above 1.5 — interviewer disagreement does not become candidate feedback.
- **Auto-send drift.** *Guard:* the skill defines no `send` action. Drafts are written to `drafts/<candidate-id>.md` for the recruiter to review, edit, and send from the ATS outbox. AI-drafted-and-sent rejection feedback without review damages [candidate experience](/en/learn/candidate-experience/) and produces incidents.
- **PII in the audit log.** *Guard:* step 6 writes only `candidate_id_hash` (SHA-256), never the raw candidate ID, name, or scorecard text. The audit line is for run reproducibility, not candidate data retention.
- **Generic boilerplate harm.** *Guard:* if step 3 cannot surface a rubric dimension that has both mean ≤ 2 and a verbatim evidence string, the skill writes the generic-decline template from `references/3-output-format.md` rather than synthesizing weak specifics. Generic decline is honest; weak specifics are worse than no specifics.
# Rubric-to-feedback mapping — TEMPLATE
> Replace this template with your team's approved candidate-facing
> phrasing per rubric dimension. The rejection-feedback skill reads
> this file in step 4 to translate scorecard language (which is
> internal, often blunt) into candidate-facing language (which must
> be specific, evidence-grounded, and EEOC-safe). Without this file
> the skill will not draft specifics — it falls back to the generic
> decline template.
## How this file is used
The skill matches each surfaced dimension (from step 3) against the `dimension_id` below, then uses the `candidate_facing_phrasing` template, substituting in the verbatim evidence string from the scorecard or transcript.
If a dimension is surfaced by step 3 but has no entry below, the skill will NOT draft specifics for it — the dimension is dropped. This forces the team to deliberate on candidate-facing phrasing once, in writing, rather than letting the LLM improvise per run.
## Dimension entries
### dimension_id: technical_depth
**internal_label**: Technical depth (1-5)
**rubric_anchors**:
- 5: Reasons fluently across multiple layers of the stack; explores tradeoffs unprompted.
- 4: Reasons clearly within their primary layer; surfaces tradeoffs when asked.
- 3: Recalls correct patterns; tradeoff reasoning needs prompting.
- 2: Recalls patterns inconsistently; tradeoff reasoning absent or shallow.
- 1: Patterns incorrect or contradicted under follow-up.
**candidate_facing_phrasing** (used for mean ≤ 2):
```
In the {round_name} round, the team was looking for {specific_topic}
as part of {specific_decision_context}, and that did not come up.
That was the dimension that drove the decision for this specific
role.
```
Substitution sources:
- `{round_name}` → from scorecard `interview_round` field
- `{specific_topic}` → from `references/2-banned-phrase-blocklist.md` approved-topics list (NEVER free-text from the LLM)
- `{specific_decision_context}` → from rubric anchor text
**candidate_facing_phrasing** (used for mean ≥ 4, opening only):
```
{Strength_observation}. {Interviewer_count_phrase} cited
{specific_evidence} specifically.
```
---
### dimension_id: system_design
**internal_label**: System design (1-5)
**rubric_anchors**:
- 5: Drives the design conversation; surfaces consistency, availability, and operational tradeoffs unprompted.
- 4: Engages with tradeoffs when prompted; covers most major axes.
- 3: Engages with tradeoffs when prompted; covers one or two axes.
- 2: Tradeoff reasoning shallow; misses major axes that the role requires.
- 1: Cannot construct a system that meets the stated requirements.
**candidate_facing_phrasing** (used for mean ≤ 2):
Same template as `technical_depth`.
---
### dimension_id: collaboration
**internal_label**: Collaboration (1-5)
**rubric_anchors**:
- 5: Specific examples of cross-functional work, named tradeoffs, named outcomes.
- 4: Specific examples, less explicit on tradeoff reasoning.
- 3: General examples, no specifics on tradeoffs or outcomes.
- 2: Vague examples or examples that do not show collaboration evidence.
- 1: No relevant examples surfaced.
**candidate_facing_phrasing** (used for mean ≤ 2):
Same template as `technical_depth`. **Constraint:** never use the words "communication", "fit", "soft skills", or "executive presence" in the candidate-facing draft for this dimension. Those terms are on the banned-phrase blocklist because they correlate with bias claims.
---
## Constraints across all dimensions
- One strength and one gap per draft, maximum. The skill caps at one of each in step 4.
- Every substitution slot is filled from a structured field (scorecard, transcript, rubric anchor) or from the approved-topics list. The LLM never free-texts a substitution value.
- Comparative ranking is not in this file and is on the blocklist. If you find yourself adding "vs other candidates" phrasing, stop and revisit the rubric anchors instead.
- Update this file when the team revises rubric anchors. The skill's audit log captures `rubric_sha256` per run, so revisions are visible in retro.
## Last edited
{YYYY-MM-DD}
# Banned-phrase blocklist
> The rejection-feedback skill greps the final draft against every
> pattern below in step 5 (bias and false-specifics screening). Any
> hit halts the run with the offending string surfaced. Do NOT edit
> this file to make a draft pass — fix the rubric, the scorecard
> language, or the rubric-to-feedback mapping instead.
## A. EEOC-implicating language
A1. **Protected-class proxies.** Any of the following terms or patterns in the draft halts the run:
- `culture fit`, `cultural fit`, `culture add` (without an accompanying behavioral-anchor citation)
- `team fit`, `not a fit` (when used as the substantive reason)
- `personality`, `chemistry`, `vibes`
- `executive presence`, `leadership presence`, `gravitas`
- `polish`, `polished`, `lacks polish`
- `aggressive`, `abrasive`, `pushy` (gendered descriptors)
- `soft`, `nice`, `quiet`, `meek` (inverse gendered descriptors)
- `mature`, `seasoned`, `young`, `energetic`, `digital native` (age proxies)
- `accent`, `articulate`, `well-spoken` (national-origin proxies)
- `family`, `kids`, `pregnant`, `maternity`, `paternity`, `parental` (family-status proxies)
- `accommodation`, `disability`, `health` (any reference to accommodation discussions in the rejection text)
- `religion`, `church`, `prayer`
- `marital`, `married`, `single`
- `name origin`, `surname` (any commentary on the candidate's name)
- `school`, `university`, `Ivy`, `tier-1`, `top-N` (when used as the substantive reason — schools may appear in factual context but not as the rejection driver)
A2. **Comparative ranking language.** Halts the run:
- `stronger candidates`, `better candidates`, `more qualified`
- `second choice`, `runner-up`, `not the top choice`
- `closer fit elsewhere`, `closer match`
- `pool was strong`, `competitive pool`
- `we found someone`, `we hired someone`, `the role is filled` (these belong in a separate sentence about the role status, not framed as a candidate ranking)
- Any phrase that implies a relative ordering of the candidate against unnamed others.
A3. **Defamation-risk language.** Halts the run:
- `dishonest`, `misleading`, `lied`, `lying`
- `unprepared`, `did not try`, `did not care`
- `arrogant`, `entitled`, `difficult`
- `concerning`, `red flag`, `worrying`
- Any subjective-character claim that could be cited against the firm in a defamation action.
## B. False-specifics patterns
B1. **Quote markers without source.** Halts the run if the draft contains any quoted string (`"…"` or `'…'`) that does not appear verbatim in the scorecard or transcript pool from step 2.
B2. **Numeric claims without source.** Halts if the draft contains a numeric claim (`scored X`, `Y out of Z`, `X% of`) — interview scores are internal calibration data, not candidate-facing content.
B3. **Interviewer-identifying claims.** Halts if the draft names an interviewer, references an interviewer's role beyond the generic "the team", or attributes a quote to a specific person. Interviewer identities are protected and naming them creates retaliation risk.
B4. **Round-identifying claims that could not have happened.** Halts if the draft references a round (`take-home`, `system design`, `behavioral`, `pair programming`) that is not present in the scorecard set for this candidate. The skill validates round names against the loop's actual structure.
## C. Process-risk language
C1. **Promises about the future.** Halts the run:
- `we will reach out`, `we'll be in touch`, `next time`
- `definitely apply again`, `you will get an offer`
- `keep your resume on file` (varies by jurisdiction whether this is permissible — neutral phrasing is "we welcome a future application")
- Any timeline commitment.
C2. **Process-improvement requests from the candidate.** Halts if the draft asks the candidate for feedback, a referral, or a testimonial. Reverse asks in a rejection email are an EEOC-witness-statement risk and a candidate-experience harm.
C3. **Unsolicited specifics in deny-jurisdiction cases.** The skill's step 1 should have caught this, but as a defense-in-depth check: if the run's `jurisdiction_policy` returned `unsolicited_feedback: deny` and `feedback_requested: false`, the draft must match the generic-decline template byte-for-byte. Any deviation halts.
## D. Approved-topics list (positive list, used by step 4)
The rubric-to-feedback mapping's `{specific_topic}` substitution slot pulls from this list. The LLM never free-texts a topic string.
- `consistency-availability tradeoffs`
- `read-replica reasoning`
- `caching layer reasoning`
- `failure-mode reasoning`
- `test coverage`
- `error-handling specificity`
- `data-modeling tradeoffs`
- `query-pattern reasoning`
- `migration sequencing`
- `deployment sequencing`
- `cross-team coordination examples`
- `tradeoff reasoning under time pressure`
Add to this list only after team review. Topics added here are permitted to appear in candidate-facing drafts.
## E. Maintenance
This file is version-controlled. The skill captures the SHA-256 of this file in the audit log per run, so the blocklist used on a given date is reproducible. If a candidate raises a claim against a specific draft, the audit log answers "was the blocklist of date X in effect at the time of the draft" — yes or no, no judgment call.
## Last edited
{YYYY-MM-DD}
# Output format
> The rejection-feedback skill writes drafts in exactly the formats
> below. The recruiter reviews and edits in their own outbox or in
> the ATS; the skill never sends.
## Routing rules
The skill picks a route per the matrix below. The recruiter can override.
| Stage reached | Seniority | feedback_requested | Default route |
|---|---|---|---|
| onsite | senior+ | true | call |
| onsite | senior+ | false | email (generic if jurisdiction denies) |
| onsite | mid / junior | true | email (specific) |
| onsite | mid / junior | false | email (generic) |
| final loop | any | any | call (overrides above) |
| referred-by-VIP | any | any | call (recruiter judgment) |
| earlier than onsite | any | any | OUT OF SCOPE — use templated decline |
`senior+` = staff, principal, manager, director. `referred-by-VIP` = candidate has a `referrer_priority: high` flag in the ATS.
## Email format — specific feedback (consent + safe jurisdiction)
```markdown
Subject: Update on your {role_title} interview at {company_name}
Hi {candidate_first_name},
Thank you for the time you invested in our interview process — the
{round_1_label}, {round_2_label}, and the conversations with the
team. We appreciated the care you put into each stage.
After the team's debrief, we have decided not to move forward with
your candidacy for this role.
You asked for feedback, so here is what stood out from the loop:
- **What went well.** {strength_phrasing_from_mapping}.
- **Where the team landed differently.** {gap_phrasing_from_mapping}.
This was the dimension that drove the team's decision.
This feedback is specific to the loop you ran with us; it is not a
ranking against other candidates and it is not a comment on your
overall engineering ability.
If a future role at {company_name} matches your background, we would
welcome your application.
Best,
{recruiter_first_name}
```
Constraints baked into this template:
- One strength, one gap. No more.
- The phrase "not a ranking against other candidates" is mandatory, because it pre-empts the most common candidate response loop ("how did I compare").
- The phrase "not a comment on your overall engineering ability" is mandatory, because it isolates the feedback to this loop and pre-empts the "you said I am bad at engineering" escalation.
- "We would welcome your application" — neutral future language. Not "we will reach out", not "next time".
## Email format — generic decline (deny jurisdiction OR no consent OR no surfacable specific)
```markdown
Subject: Update on your {role_title} interview at {company_name}
Hi {candidate_first_name},
Thank you for the time you invested in our interview process. We
appreciated the care you put into each stage.
After the team's debrief, we have decided not to move forward with
your candidacy for this role.
If a future role at {company_name} matches your background, we
would welcome your application.
Best,
{recruiter_first_name}
```
This is the safe default. The skill writes this template byte-for-byte when:
- `jurisdiction_policy` returned `unsolicited_feedback: deny` and `feedback_requested: false`
- step 3 surfaced no rubric dimension with both `mean ≤ 2` AND a verbatim evidence string
- a legal flag on the candidate file is present
- the loop has under two signed-off scorecards
Generic decline is honest. Weak specifics are worse than no specifics.
## Call-notes format
```markdown
# Call notes — {candidate_first_name} {candidate_last_initial}. ({role_title})
## Frame
- Open with thanks for the time invested.
- Lead with the strength: {strength_phrasing_from_mapping}.
- Single gap: {gap_topic_from_approved_list}. One sentence, no piling
on.
## Suggested phrasing for the gap
"{gap_phrasing_from_mapping}"
## Likely candidate questions
Q: "Was there anything I could have done differently?"
A: Acknowledge the question. Refer back to the single gap. Do NOT
add new feedback dimensions on the call — anything not in the
written draft is off-script and creates inconsistency risk.
Q: "Will you keep me in mind for future roles?"
A: Yes if true; specifics on what kind of role. Do NOT promise a
timeline.
Q: "Can I get a second-look interview?"
A: No. The decision is final. The recruiter reiterates appreciation
and closes.
Q: "Who else interviewed?"
A: Decline. Interviewer identities are protected. "I cannot share
that, but I can tell you the team weighed the input from every
round."
Q: "What did interviewer X think?"
A: Decline. Same reason. "I cannot break out individual scores; the
decision was a team decision."
## Off-script
If the candidate raises a discrimination concern, comparative-ranking
question, or accommodation issue, the recruiter says "let me come
back to you on that" and routes to HR / counsel. The recruiter does
NOT improvise an answer.
## Call duration target
10-15 minutes. Past 20 minutes, the call is no longer feedback —
it is an extended negotiation about the decision, and that is not
a useful place to be.
```
## Audit-log line format
One JSON object per line in `audit/<YYYY-MM>.jsonl`:
```json
{
"run_id": "uuid-v4",
"candidate_id_hash": "sha256-of-candidate-id",
"role_id": "role-slug",
"jurisdiction": "US-CA",
"feedback_requested": true,
"route": "email",
"rubric_sha256": "abcdef...",
"blocklist_sha256": "abcdef...",
"mapping_sha256": "abcdef...",
"dimensions_surfaced": ["technical_depth"],
"blocklist_hits": 0,
"model_id": "claude-sonnet-4-5",
"timestamp": "2026-05-03T14:00:00Z"
}
```
No raw candidate ID, no candidate name, no scorecard text, no draft text. The audit log is for run reproducibility, not data retention. Candidate-facing drafts live in `drafts/<id>.md` under the recruiter's own retention policy.
## Last edited
{YYYY-MM-DD}