Aller au contenu principal

Model cards publiques

Registre des algorithmes

Model cards publiques de 7 de nos systèmes de scoring : les données que chacun utilise, les attributs qu'il n'utilise jamais, ses pondérations de score et ses limites connues. Elles sont tenues à la main et ne constituent pas un inventaire de toutes les fonctionnalités d'IA de la plateforme.

Classification : Le règlement UE 2024/1689 (AI Act) classe à haut risque les systèmes qui évaluent des candidats à l’emploi (annexe III §4). Nous plaçons nos modèles de matching dans cette catégorie de notre propre appréciation. Il s’agit d’une auto-évaluation : nous ne disposons d’aucune évaluation de conformité par un tiers ni d’aucune certification.

Talent Match

v1.2.0 · Rule-based scoring

Finalité

Rank students for a given role based on verified skills, projects and stage experience.

Public : Recruiters searching for candidates. The student can read the explanation for any match concerning them.

Données utilisées, pondérations, supervision et droits

Données d'entrée utilisées

  • Required skills — from Job posting
  • Preferred skills — from Job posting
  • Student verified skills — from SkillDelta graph
  • Student skills (self-declared) — from Student profile
  • Teacher-evaluated projects — from Project.verificationStatus — set only when a teacher's evaluation of the project, given at the student's request, stands; the school confirms an unknown teacher's identity, never the project
  • Stage supervisor ratings — from StageExperience
  • Graduation year — from Student profile
  • Location — from Student profile

Jamais utilisées

  • Gender
  • Nationality
  • Ethnicity
  • Religion
  • Age (beyond graduation-year class)
  • Photo / any biometric inference
  • GPA — never used: it earns no points in the score, and the minimum-GPA search parameter is accepted by the API but not applied to the query

Pondérations de score

Required skills match
max. 40 pts
Preferred skills match
max. 15 pts
Verified depth bonus (Advanced/Expert)
max. 10 pts
Verified projects with matching skills
max. 20 pts
Stage / internship experience
max. 15 pts

Points out of a 100-point match score. Verified skills count at full weight, self-declared ones at 0.6.

Supervision humaine

Matching runs without a human step in front of it. Review happens after the fact: you can contest a match from its explanation page, which opens a request with a reference and a date, and the reviewer's outcome is written back onto the stored explanation.

Vos droits en tant que personne concernée

  • Right to see the explanation for any match concerning you (/matches/[id]/why). A record is stored for every candidate a search scores, not only the ones the recruiter was shown — your own list shows the matches you were surfaced in, and your data export contains the rest.
  • Right to contest a match and ask for human review, from the match page or /dashboard/student/privacy/requests. You get a reference and the date we owe you an answer by; the outcome is recorded against the explanation itself.

Tests de biais

None. Gender and ethnicity are not fields in our schema, so no parity test across them is possible. We do hold nationality and date of birth as optional CV fields, and university, degree and graduation year — parity across those could be tested on data we already have, and is not.

Contexte réglementaire

  • EU AI Act Reg. 2024/1689, Annex III §4
  • GDPR Art. 22
  • EU AI Act Art. 86 (right to explanation)

Instruments auxquels nous nous référons, et non des obligations que nous déclarons remplies.

Placement Prediction

v0.9.0 (preview) · Hybrid scoring

Finalité

Estimate a student's probability of securing a job offer within 6 months post-graduation.

Public : The student and their university career service. No recruiter surface shows the number: the badge left the candidate page and the decision-pack header on 2026-09-02, and the same day the two doors that had been missed were closed — the compare view, where it was relabelled "fit score" and used to rank candidates against each other, and the portfolio Trust Score, which carried it as a tenth of its weight (defaulting to 30 out of 100 for the majority of students, who have no prediction at all). /api/predictions is now readable by the student themselves, their career service and admins only; the RECRUITER role is refused. It still appears in the decision-pack PDF, where it is labelled "Fit signal" and carries the note that the weights are hand-set, never fitted to data, and not a probability of being hired. The underlying factors still render to recruiters; the headline percentage does not.

Données utilisées, pondérations, supervision et droits

Données d'entrée utilisées

  • Hireability score — from SkillPathRecommendation
  • Verified project ratio — from Project.verificationStatus
  • AI project analysis (innovation, complexity, market relevance) — from Project
  • High-priority skill gaps (lowers the estimate) — from SkillPathRecommendation
  • Teacher evaluations (answered at the student's request — an evaluation, not a confirmation) — from ProfessorEndorsement
  • Discipline market demand — from Project.discipline, scored against a hand-set constant per discipline — not derived from live job postings
  • Prior confirmed placements — from Placement

Jamais utilisées

  • Gender
  • Nationality
  • Ethnicity
  • Religion
  • Family background
  • Socio-economic data
  • GPA

Pondérations de score

Base rate (applied to everyone)
max. 30 pts
Hireability score
max. 25 pts
Verified project ratio
max. 15 pts
AI project analysis
max. 15 pts
High-priority skill gaps
max. 10 pts
Teacher evaluations
max. 10 pts
Discipline market demand
max. 5 pts
Prior confirmed placement (flat bonus)
max. 5 pts

Share of the estimated probability, in percentage points. The base rate applies before any signal; skill gaps subtract rather than add; the placement bonus is flat once a student has at least one confirmed placement and does not scale with the count. A student with no skill-path record, or one whose record lists no gaps, is scored at half gap severity; a student with no projects is scored at a mid-range discipline demand. Both are defaults applied when the data is missing, not measurements.

Supervision humaine

The prediction is advisory and is not by itself a decision. It is no longer reachable by a recruiter at all — not as a badge, a fit score, a trust-score component or an API response — so the oversight question is now confined to the student and the career service reading their own figure, and to the labelled "Fit signal" in the decision-pack PDF.

Vos droits en tant que personne concernée

  • Right to view your own prediction and the signals behind it
  • Right to contest it and ask for human review, from /dashboard/student/privacy/requests

Tests de biais

None.

Contexte réglementaire

  • EU AI Act Reg. 2024/1689, Annex III §4
  • GDPR Art. 22

Instruments auxquels nous nous référons, et non des obligations que nous déclarons remplies.

Ranked Candidate Search

v1.0.0 · Hybrid (fixed-weight scoring over a partly model-built input set)

Finalité

Score every student in a bounded candidate pool against a recruiter's job posting — or, when no posting is selected, against the skills the recruiter typed into the search box — and, in the job case, sort named people by the result.

Public : Recruiters and admins; every other role is refused (route.ts:55). The scored student can read their own record, but not all of it in one place: a row scored against a chosen posting and returned on the page the recruiter saw appears in their match list (/api/student/matches requires surfaced: true, counterpartyType RECRUITER and contextType JOB, that file's lines 33-52), while a row from a search with no posting attached, and every row that was scored but never returned, is reachable only by opening its own explanation page or in the GDPR data export.

Données utilisées, pondérations, supervision et droits

Données d'entrée utilisées

  • Required skills — from Job.requiredSkills, or the comma-split terms the recruiter typed into the skills / hardSkills / softSkills / tools / technologies boxes when no posting is chosen (route.ts:184-201)
  • Preferred skills — from Job.preferredSkills. A search-box query supplies none, so this factor is absent from its score (route.ts:196)
  • Target disciplines — from Job.targetDisciplines. A search-box query supplies none
  • Student project skill terms (technologies, skills, tools, competencies) — from The student's own public projects, capped at the 20 most recently created (route.ts:265-322), flattened into one lowercased set (lib/job-matching.ts:80-95). Student-typed: the AI project reading writes scores and insights, not these arrays
  • Skill synonym expansion — from The SkillMapping table, loaded whole into an in-memory index and applied to every candidate's skill set (lib/job-matching.ts:282-327). Rows can be model-generated and cached with aiGenerated: true (lib/skill-translation.ts:41-58); the index does not distinguish them from curated ones
  • Project verification status — from Project.verificationStatus === 'VERIFIED' on those same public projects (route.ts:433). Drives both the evidence multiplier and the depth bonus
  • Project discipline — from Project.discipline, collected across the public projects (lib/job-matching.ts:163-171)
  • Model-extracted skill terms (pool selection only — earns no points) — from StudentSkillIndex, queried by the facet filters that decide who is fetched: its hardSkills bucket includes the competencies the model detected in a project, and its softSkills bucket is written only when a model actually read the project (lib/student-skill-profile.ts:153, 162). A recruiter filtering on a soft skill is searching a set no human wrote

Jamais utilisées

  • Gender — not a field in our schema
  • Ethnicity — not a field in our schema
  • Religion — not a field in our schema
  • Nationality and date of birth — held as optional CV fields elsewhere on the platform; this route does not select them
  • GPA — read and filterable: the gpaMin parameter removes candidates from the pool after the fetch (route.ts:354-360). It earns zero points in the score
  • University, degree, major, graduation year and location — read as WHERE filters (lib/recruiter-student-filter.ts:113-163). They decide who is scored; they earn zero points
  • Job-search status — the availability signal the student sets themselves. It is filterable and it is the first key of the database fetch (route.ts:333), so when the population exceeds the 300-candidate pool it decides who gets scored at all. It earns zero points
  • Subscription tier — not read. The route does not select the student's plan, so it can neither score nor order on it. Until 2026-10-02 the pool was sorted STUDENT_PREMIUM first before any scoring, and the stable ranked sort kept that paying-first order among equal scores; both were removed that day, along with the "priority in search results" row the student pricing page sold
  • Teacher evaluations — counted through the project they evaluated, and only there: since 2026-10-10 a teacher's evaluation that stands, given at the student's request, is the only thing that sets Project.verificationStatus (lib/project-evaluation), which the evidence multiplier, the depth bonus and the candidate's verified badge count (route.ts:470, 503, 777). The evaluations themselves also order the projects a recruiter sees (route.ts:757-765); their rating is never scored
  • Photo, name and email — selected from the profile and then dropped by the anonymized projection before the response (route.ts:638). Never scored, and no inference is drawn from a photo
  • AI project scores (innovationScore, complexityScore, market relevance) — not read by this scorer at all: the project select does not fetch them, and the projects it sees are picked by creation date (route.ts:265-322)

Pondérations de score

Required skills matched (share of the posting's required list)
max. 60 pts
Preferred skills matched
max. 25 pts
Discipline match
max. 15 pts
Verified-depth bonus (3 points per teacher-evaluated project)
max. 10 pts

The first three are percentage weights on a 0-100 base, renormalized over only the categories the posting actually specifies (lib/job-matching.ts:258-266) — a search-box query names required skills and nothing else, so required skills are the whole of its base. The base is then multiplied by an evidence multiplier of 0.6 + 0.4 × (share of the matched required skills that a VERIFIED project backs), so a match made entirely of self-declared skills keeps 60% of its base (route.ts:461-468). The depth bonus is 3 points per teacher-evaluated project up to a ceiling of 10, added after the multiplier, and the total is capped at 100 (route.ts:471-473). Labels are cut at 80 / 60 / 40 (lib/match-explanation.ts:269-274). No model runs while the score is computed — every constant above is in the source — but the input set is partly model-built, which is why the type is hybrid and not rule-based: the synonym expansion applied to a student's skills comes from a table whose rows may be model-generated, and the facet filters that decide who is in the pool read an index whose soft-skill terms exist only because a model wrote them. The factor rows stored with every explanation RECONSTRUCT the stored score: their contributions sum to it exactly. Until 2026-09-11 they did not — they declared weights of 60 / 25 / 10 / 5 and computed their own contributions, which disagreed with this note in two ways at once (discipline is weighted 15 and was published there as 5) and omitted the multiplier, the renormalization and the cap entirely. The rows now come from the same function that owns the weights (lib/job-matching.ts:258-280, which also folds the rounding residual into its last term), and the multiplier, the depth bonus and the cap are each their own row carrying the points they moved — the multiplier's contribution is negative whenever it is below 1.0, which is the ordinary case (route.ts:512-572). __tests__/unit/match-score-reconstructs.test.ts asserts the sum over randomised jobs rather than trusting this paragraph. Finally, at most 20 of a student's public projects are read, the most recently created ones, so a student with more than twenty is scored on their newest twenty.

Supervision humaine

No human reviews the ranking before it reaches the recruiter. The scores are computed, the list is sorted and the response is returned in one request; the audit record is written afterwards and fire-and-forget, so a failed write costs the record rather than the search (route.ts:592-608). Review exists only after the fact, and it does not re-run or re-order a search that has already been returned. Two routes to it: the student can contest a match, which opens a rights request with a reference and a due date and writes CONFIRMED / OVERRIDDEN / FLAGGED back onto the stored explanation when it is closed (lib/rights-request.ts:344-366); and the student's own university — matched on the university name on their profile — or a platform admin can review a single explanation directly (PATCH /api/match/[id]/audit, lines 64-104).

Vos droits en tant que personne concernée

  • Right to see the explanation for any match concerning you (/matches/[id]/why, which resolves any row by id for its subject — /api/match/[id]/why:17). A row is stored for every candidate a run scored, not only the ones the recruiter was shown; the record's `surfaced` flag says which, and it means the row was returned in the result set, not that a person read it.
  • Right to contest a match and ask for human review, from the match page or /dashboard/student/privacy/requests. You get a reference and the date we owe you an answer by; the outcome is recorded against the explanation itself.
  • Right to the whole set in your data export. Your match list deliberately shows only job-ranked rows you were surfaced in, so the rows from a recruiter's plain skill search — scored and stored exactly the same way — appear only in the export or on their own explanation page.
  • What the record does not contain, stated because it limits the three rights above: this scorer writes no subject snapshot, so a row lists the four factors and the score but not which of your fields were read; it writes an empty input snapshot, so the university audit view holds no record of the criteria the search used; and a job-ranked run scores up to 300 candidates while at most 200 rows per run are stored, surfaced ones first (lib/match-explanation.ts:74, 351-358), so the tail of a large run leaves no record at all.

Tests de biais

None. Gender and ethnicity are not fields in our schema, so no parity test across them is possible. This scorer reads university, degree, graduation year and location as filters, and those are exactly the axes a parity test could use on data we already hold — no such test is run. Two effects built into the arithmetic are equally unmeasured: the evidence multiplier holds a candidate whose matched skills are all self-declared to 60% of their base score, which favours students whose teachers evaluate their work over students whose teachers do not; and the candidate pool is ordered paying-subscribers-first before scoring, so where two candidates finish on the same score the paying one is listed above the other.

Contexte réglementaire

  • EU AI Act Reg. 2024/1689, Annex III §4(a)
  • EU AI Act Art. 6(3) — no Art. 6(4) derogation is claimed: the system profiles natural persons
  • EU AI Act Art. 12 (logging) — one MatchExplanation row per candidate scored, grouped by runId
  • EU AI Act Art. 14 (human oversight) — after the fact only, see above
  • EU AI Act Art. 86 (right to explanation)
  • GDPR Art. 22

Instruments auxquels nous nous référons, et non des obligations que nous déclarons remplies.

AI Shortlist

v1.0.0 · Rule-based scoring

Finalité

Rank public student profiles against a free-text role form — a title, a description and a list of skills typed into the AI Shortlist / AI Talent Search panel — and return the ten highest scorers, anonymized. There is no posted job behind it. Despite the product name, no model runs at scoring time: the score is a fixed sum of six terms, and the only semantics in it come from a hand-maintained synonym and adjacency table, so "React.js" matches "React" and "Milan" does not match "Milano". Nothing model-produced selects the inputs either — every public project on a profile is read, not a top-N slice ranked by an AI score. The pool is capped at 1,000 public student profiles and the query specifies no ordering, so once more than 1,000 exist, which of them are scored at all is whatever order the database returns. Of the candidates that survive scoring, only ranks 1-40 are checked against the access gate, and the first ten of those that pass are returned.

Public : Recruiters and admins; the endpoint refuses every other role. A shortlist result is always anonymized: no name, photo, email, phone or location leaves the endpoint — only "Fabio C.", the university, the degree, the graduation year, a capped skill list and the score. The scored student can read the record kept of their own score, but not from their match list: these rows are written with context type GENERIC, because a role form is not a posting, and the student match list renders only JOB rows. In practice a student reaches one of these rows through a data export, or through its explanation page if they already have the id.

Données utilisées, pondérations, supervision et droits

Données d'entrée utilisées

  • Requested skills — from Recruiter's role form (POST body `skills`) — at least one is required
  • Location (optional) — from Recruiter's role form, tested as a case-insensitive substring in either direction against the candidate's free-text location
  • Minimum average grade (optional) — from Recruiter's role form; applied to the candidate's average normalized project grade, and only to candidates who have at least one graded project
  • Self-declared skills — from Student profile (User.skills)
  • Project skills, technologies and competencies — from The student's public projects. Entered by the student — the AI project analyser writes innovation/complexity scores and its insights blob, not these three columns
  • Verified projects — from Project.verificationStatus = VERIFIED, set since 2026-10-10 only when a teacher's evaluation of the project, given at the student's request, stands (lib/project-evaluation). From 2026-10-01 to 2026-10-10 institution staff set it by verifying the project; before 2026-10-01 either could
  • Teacher-evaluated projects — from Project.universityVerified, written together with verificationStatus by the same teacher's evaluation (the field name is older than the rule; no school confirms a project)
  • Average normalized project grade — from Project.normalizedGrade — the grade the student typed in, converted to a 0-100 scale by a country conversion table
  • Profile completeness signals — from Presence, not content, of first name, last name, university, degree, bio, photo, at least one skill and at least one project
  • Candidate location — from Student profile (User.location), free text

Jamais utilisées

  • Gender, ethnicity, religion — not columns in our schema, so the scorer could not read them if it tried.
  • Nationality and date of birth — optional CV fields we do hold on the student record. This endpoint does not select them and does not score them.
  • GPA — not selected at all here. The minimum-grade parameter is real and is applied, but to the average normalized grade of the candidate's public projects, never to GPA.
  • Graduation year — READ AND SHOWN, NEVER SCORED. It is fetched for the candidate card the recruiter sees and earns zero points; it is also absent from the list of fields the stored explanation says were read.
  • The role description — required by the API and saved onto a saved shortlist, never scored. It is deliberately left out of the recorded search criteria, because recording it would present it as a filter that was applied to people when it was not.
  • Role title — recorded on the explanation record as the context of the run. Not matched against anything.
  • The photo itself — read only as a yes/no completeness signal worth 1 point. No image is analysed, and no photo is returned on a shortlist run.
  • University and degree names — the same: 1 point each for being non-empty. They are never matched, ranked or compared, and no institution is preferred over another.
  • Bio text — 1 point for existing; the words are never read.
  • AI project analysis (innovation, complexity, market relevance) — written on every analysed project and used by other scorers on this platform. This endpoint does not select those columns.

Pondérations de score

Requested skills matched
max. 35 pts
Verified projects (5 each)
max. 20 pts
Average project grade
max. 15 pts
Teacher-evaluated bonus (flat)
max. 10 pts
Profile completeness
max. 10 pts
Location match (flat)
max. 5 pts

Points out of a match score capped at 100. The skills term is round(exact_ratio × 35 + adjacent_ratio × 15) over one shared denominator, the number of skills the recruiter typed; a skill counts as exact or adjacent, never both, so the two ratios cannot both be 1 and 35 is the real ceiling — the comment above that line in the code says "up to 40 points" and is wrong. The six maxima sum to 95, so the 100-point cap is unreachable and no one has ever been capped by it. The two verification terms are not independent: every code path that marks a project VERIFIED sets universityVerified in the same write, so one evaluated project normally earns 5 + 10, and four earn 20 + 10. Grade points scale linearly with the average (an average of 100 earns 15); a candidate with no graded project earns 0 there and is also not filtered by the minimum-grade parameter, which only tests candidates who have a graded project. Completeness is 1 point each for first name, last name, university, degree, bio and photo, plus 2 for having any skill and 2 for having any project. A candidate with no exact or adjacent skill match is dropped from the result entirely, whatever the other 60 points say.

Supervision humaine

None before the recruiter sees it. One request scores the pool, sorts it, cuts it to ten and returns it; nothing queues, samples or approves a shortlist, and the audit record is written after the response has already been assembled and is explicitly allowed to fail without affecting it. Review exists only afterwards and only if someone starts it: a university (for its own students) or an admin can open any stored explanation and record an outcome — CONFIRMED, OVERRIDDEN or FLAGGED — onto the row, and a student can file a contest or human-review request against a row they own, which is checked against the record before it is accepted. Nothing in the product forces either to happen, and the recruiter has seen the ranking long before it could.

Vos droits en tant que personne concernée

  • Right to see the explanation of your own score at /matches/{id}/why: the factors with the points each one contributed, the score, whether the row was surfaced — meaning returned in the recruiter's result set, which is not the same as a human having read it — and a snapshot of which of your fields were read. The recruiter's own search criteria are withheld from that view; they are the searcher's, not yours.
  • A caveat we would rather state than have you find out: these rows do not appear on your match list. That list shows only rows tied to a posted job, and a typed role form is not one. The practical routes to an ai-shortlist row are your data export (it carries up to the 5,000 most recent explanation records) or a direct link to its explanation page.
  • Right to contest a score and ask for human review, from the match page or /dashboard/student/privacy/requests. Ownership of the row is verified against the record before the request opens, and the reviewer's outcome is written back onto the explanation itself.
  • Not every scored candidate gets a row. One run writes at most 200 explanation records — the returned ten first, then the rest in score order — so a run that scores more than 200 people leaves the tail with no record at all. Candidates the access gate refused (a block, a likely minor without consent, an out-of-scope institution) are deliberately not recorded either: writing a row saying this company scored you would be the processing the gate had just refused.
  • Your profile is only in this pool while it is public. A profile that is not public is never fetched, never scored and never recorded.

Tests de biais

None. Gender and ethnicity are not fields in our schema, so no parity test across them is possible. Nationality and date of birth we do hold as optional CV fields, and university, degree, graduation year and country sit on every profile — parity across those could be tested on the very records this scorer writes, and is not. Three of its terms should be tested before anyone calls it neutral. Ten of the 95 attainable points are for how filled-in a profile is — a photo, a bio, a filled university field — which measures effort on our own form, not the candidate. The grade term runs a self-entered grade through a country-specific conversion table, so it compares grading systems as much as it compares students, and it defaults to the Italian scale when no country is set. And the location term is a substring test on free text, which silently rewards candidates who happen to spell their city the way the recruiter did.

Contexte réglementaire

  • EU AI Act Reg. 2024/1689, Annex III §4
  • GDPR Art. 22
  • EU AI Act Art. 86 (right to explanation)

Instruments auxquels nous nous référons, et non des obligations que nous déclarons remplies.

University Position Match

v3.0.0 · Rule-based scoring

Finalité

Rank an institution's own students against a role a member of its staff describes — skills typed as tags, a free-text description, or both — as a preview of who the cohort could put forward. No employer runs it and no employer sees the result.

Public : Career-office and university staff at the institution the students belong to (role UNIVERSITY, or ADMIN). The output never reaches a recruiter or a company: it renders inside the institution's own matching dashboard and nowhere else. The scored student can read their own record, but nothing in their dashboard links to it — the student match list and the "Matches (30d)" tile both filter to counterpartyType RECRUITER and this scorer writes INSTITUTION, deliberately, so a career-office preview is not shown to the student as an employer looking at them. The record reaches the student through the GDPR export, which carries its id; the explanation page then resolves by that id.

Données utilisées, pondérations, supervision et droits

Données d'entrée utilisées

  • Role skills — from Chosen on the staff form from suggestions (lib/institution-skill-suggestions): the skills the institution's students show on their profiles, public projects and those projects' courses, the skills active public postings name, and the curated SkillMapping vocabulary, merged under one canonical key and shown with how many students and postings use each. A term matching none of them can still be added and is marked unrecognised. The skills can also be filled from a ready-made selection, browsed by area (lib/role-areas classifies job titles into seventeen areas): a role that recurs on the board, with the skills at least a quarter of its postings name and at least twice as often as the board overall (generic advert words excluded), or one of the institution's programmes, with the skills at least four in ten of its students show. A pasted description is read against that same set (at most 15, longest match first) and its skills are shown for the staff member to add before the search. Nothing is generated.
  • Student skills (self-declared) — from User.skills — what the CV upload and onboarding wrote on the profile
  • Project skills and technologies — from Project — public projects only (isPublic: true); a private project is not read at all, because this list is read by staff
  • Course competencies — from Course.competencies of the courses linked to those public projects
  • Degree title — from User.degree, split into the pieces the skill canonicaliser recognises; the title itself is not a skill
  • Verified projects — from Project.verificationStatus = VERIFIED — used only to order students with the same score
  • Place — from User.city, else User.location, compared with the place the staff member typed — used only to order students with the same score

Jamais utilisées

  • Gender
  • Ethnicity
  • Religion
  • Nationality
  • Age / date of birth — including the birth date asked at sign-up, which only the minor protections read
  • Fit profile — not read. Version 2 loaded it and scored nothing with it; version 3 does not load it.
  • GPA and project grades — not read. Version 2 added up to 6 points for a grade average; that term is gone.
  • Programme level and project disciplines — not read. Version 2's education penalty and discipline bonus were reachable only through the API, never through the page, and are gone.
  • Graduation year, photo, programme name — returned and shown to staff on each result, never scored
  • Private projects — never read in this direction

Pondérations de score

Share of the role's skills the student evidences (profile, public projects, courses, degree title)
max. 100 pts
Order among equal scores: role skills shown in a teacher-evaluated project
max. 0 pts
Order among equal scores: role skills shown in any public project
max. 0 pts
Order among equal scores: lives in the place typed
max. 0 pts

The score is the number of the role's skills the student evidences, divided by the number of role skills, times 100 — the same rule and the same evidence as the skills field of the student board (lib/jobs/match-score, buildMatchBasis), compared through the skill canonicaliser, so 'JS' and 'JavaScript' are one skill and ESCO-mapped skills match across languages. There is no partial credit for a related skill and no bonus: a student either shows a skill somewhere the scorer reads or does not, and none of the four sources counts more than another in the number. With three role skills the only possible scores are 0, 33, 67 and 100, so many students share one; students with the same score are ordered, never re-scored — more role skills shown in a teacher-evaluated project first, then more shown in any public project, then living in the place typed, then by name. A student with nothing to compare (no profile skills, public projects, courses or degree title) gets no score, is listed last and is counted separately on the page. One run scores up to 1000 students and returns the best 50; the page shows 25 at a time and filters by minimum score without a new run. Version 2 (until 2026-09-14) capped a complete skill match at 80 points and added bonuses, which put 23 of a 25-student result list on exactly 88, and read the description field not at all. No model runs in this mode, and a run takes no AI credit (it took one until 2026-10-21).

Supervision humaine

No human reviews the ranking before it is shown. The staff member who described the role gets the ranked list the moment the request returns — no queue, no approval step — and the score is what decides the order and what the minimum-score filter on that page filters on. Review is only ever after the fact, through two doors. University or admin staff can open any explanation for their own students at /api/match/[id]/audit and record CONFIRMED, OVERRIDDEN or FLAGGED against it. The student can file a rights request naming the explanation id, which an admin must close with a written answer, and closing it writes the outcome back onto the explanation's own oversight columns so the two records cannot disagree. Neither door is reachable from the student's dashboard for this scorer, because nothing there links to an INSTITUTION row.

Vos droits en tant que personne concernée

  • Right to see the record. One is written for every student a run scored, returned or not, holding the score, which of the role's skills you evidenced and where (a named public project, your profile, a course, your degree title), the evidence that ordered you among students with the same score, the model version, and a snapshot of your own record as it stood at scoring time (skill list, public project and verified-project counts, location). The staff member's criteria are stripped before it reaches you.
  • Route to it: the record is not linked from your match list, which shows employer matches only, so the way in is the data export at /api/user/data-export — it carries the id — and then /dashboard/student/matches/[id].
  • Right to contest it and ask for human review, from that page or /dashboard/student/privacy/requests. You get a reference and a due date, and the outcome is recorded against the explanation itself.
  • Limit worth knowing: a run scores up to 1000 students and writes at most 200 records, keeping the 50 that were returned to staff first and then the highest-scoring. If you were scored in a larger run and placed near the bottom of it, no record of your score exists for you to read.
  • Records are deleted 731 days after they are written, unless a human review has been recorded against them, and are erased with the account.

Tests de biais

None. This scorer ranks one institution's own cohort, so the parity questions it raises are internal ones — whether the same role ranks one degree programme or institution type below another. The run id groups a search's rows and each row points at the student record by subjectId, so a within-cohort parity test needs no new field, and is not run. Gender and ethnicity are not fields in our schema, so no parity test across them is possible; nationality and date of birth are held and not tested. Four structural effects are known and unmeasured. Evidence has to be public: a student who keeps projects private is compared on profile skills, courses and degree title alone. A skill counts as present or absent, so a student who typed a skill on their profile scores the same as one who built three teacher-evaluated projects with it; only the order among equal scores separates them, and that order favours students whose teachers evaluate their work. The place tie-break reads the city a student typed, so it favours students who filled it in. And the pool is capped at the 1000 most recently created student accounts of the institution, so in a tenant above that size older accounts are never scored.

Contexte réglementaire

  • EU AI Act Reg. 2024/1689, Annex III §4
  • EU AI Act Art. 14 (human oversight)
  • EU AI Act Art. 86 (right to explanation)
  • GDPR Art. 22

Instruments auxquels nous nous référons, et non des obligations que nous déclarons remplies.

Institutional Job Match

v3.0.0 · Rule-based scoring (the Student Job Match scorer, no model call)

Finalité

Rank the active public postings on the board against one of an institution's own students, so career-service staff can see where that student fits. It runs the board's own scorer — Student Job Match (student-job-match-v1.1.0), scoreJobForStudent in lib/jobs/match-breakdown.ts — over the pool the student's ranked board uses: active public postings, up to 20,000, newest first. Staff therefore read the number the student reads for the same posting, with one stated difference: staff see only the student's public projects as evidence, so a student with private projects can see a different number on their own board. Postings routed to the institution are flagged on the page, not ranked higher.

Public : The career-service, ITS or school staff member who ran it, on their own screen. No employer sees it: every row is written with counterpartyType INSTITUTION and counterpartyId set to that staff member, and the recruiter read path matches on counterpartyId, so the employer whose posting was ranked cannot fetch these rows. The student is not told the run happened — nothing notifies them, and both their match list and the "matches (30d)" tile on their dashboard filter to counterpartyType RECRUITER, so an institutional run appears on neither. They can still read every row about them: the explanation page resolves any row whose subject is them, and the data export contains all of them.

Données utilisées, pondérations, supervision et droits

Données d'entrée utilisées

  • The eleven Student Job Match fields — from Skills, field of study, level, location, work mode, contract, languages, industry, company size, motivation and culture, each read exactly as the Student Job Match card describes, with each side's sources listed there
  • Skill evidence — from The student's public projects (isPublic: true) and the courses linked to them, profile skills (User.skills) and the degree title — the student's own board also reads their private and shared projects
  • Postings — from Active public postings in the viewer's world (a demo institution sees demo postings, a real one real postings), up to 20,000 by postedAt, newest first — the same pool and order the student board scores
  • Institution flag — from Job.institutionId equal to the staff member's institution — shown as a label, never scored

Jamais utilisées

  • Gender
  • Nationality
  • Ethnicity
  • Religion
  • Age / date of birth — including the birth date asked at sign-up, which only the minor protections read
  • GPA, grades, graduation year, university name — not read, as on the student's own board
  • Private projects — not read in this direction
  • Model calls — none. Version 2 could trigger up to 8 Claude extractions of job signals per request; this version reads only what is already cached on a posting, as the board does.
  • Routing to the institution — flagged, not scored

Pondérations de score

Skills evidenced (share of the posting's list)
max. 35 pts
Field of study
max. 12 pts
Level
max. 10 pts
Location
max. 10 pts
Contract
max. 8 pts
Languages
max. 8 pts
Work mode
max. 5 pts
Industry
max. 5 pts
Motivation
max. 3 pts
Company size
max. 2 pts
Culture
max. 2 pts

The Student Job Match weights and rules, unchanged: a weighted mean over the fields both sides stated, renormalised, with no number unless at least three fields were compared, or skills were and the posting names at least three, and 0 on a dealbreaker. The run returns the best 150 postings with all eleven fields, their reasons (worded for staff rather than for the student) and the skill evidence; filtering them on the page by country, contract, work mode or institution routing uses those 150 and does not run the scorer again. Version 2 (until 2026-09-14) scored only postings routed to the institution — 18 in the whole database that day, none for most schools — with a different eight-axis formula, so the tab was empty for almost every institution and disagreed with the student's own board where it was not.

Supervision humaine

No human reviews the score before it is shown. The staff member's list is rendered straight from the API response, ordered by the number, with no review step in front of it. After the fact there are two routes and both are narrow. A student who has the row's id can contest it, and closing that request as UPHELD / OVERTURNED / PARTIALLY_UPHELD writes CONFIRMED / OVERRIDDEN / FLAGGED back onto the explanation itself. University staff and admins can open the audit view of a row and record the same three outcomes directly — but that endpoint scopes a university by raw string equality between the student's university field and the staff account's company string, which is stricter than the resolver the matcher itself uses, so an institution that was matched by alias or name key can run this scorer and still be refused the audit view for the rows it produced. In practice the reviewer is a platform admin, reached through a rights request.

Vos droits en tant que personne concernée

  • Right to see the explanation for a posting you were scored against. A run scores up to 20,000 postings and writes a row for the 200 best-scoring, the 150 returned to staff first, each with all eleven fields and their reasons; postings below that leave no record. The rows will not appear in your match list, which shows employer matches only; you reach them through your data export or a direct link to their explanation page.
  • Right to a copy: your export carries these rows (the 5,000 most recent explanations about you).
  • Right to contest one and ask for human review, from /dashboard/student/privacy/requests or the contest dialog on the explanation page. The outcome is recorded against the explanation itself.

Tests de biais

None, and this scorer adds nothing of its own to test: it is Student Job Match with public-only evidence, so every parity question on that card applies here unchanged. The one difference is itself a known and unmeasured effect — a student who keeps projects private is shown to their career service with less evidence than they see themselves, which lowers the skills field for exactly the students who chose privacy. Gender and ethnicity are not fields in our schema; nationality and date of birth are held and not tested.

Contexte réglementaire

  • EU AI Act Reg. 2024/1689, Annex III §4
  • GDPR Art. 22
  • EU AI Act Art. 86 (right to explanation)
  • EU AI Act Art. 14 (human oversight)

Instruments auxquels nous nous référons, et non des obligations que nous déclarons remplies.

Student Job Match

v1.1.0 · Rule-based scoring (no model call at scoring time)

Finalité

Score every active, public posting against one student and sort by the result — for the student's own surfaces and, through Institutional Job Match (university-match-student-v3.0.0, public projects only), for their institution's career staff. The student's surfaces: the ranked board (/api/dashboard/student/jobs, up to 20,000 postings per request, the best 500 returned), the role detail page (/api/jobs/[id] for a STUDENT viewer), the saved-search alert cron, the new-offer alert (a company's offer that just went live, lib/jobs/new-offer-alerts), the daily agent and the roles-for-you feed. One function — scoreJobForStudent in lib/jobs/match-breakdown.ts — for all six, so the same student and the same posting never read two different numbers. Twelve fields are compared; each is scored only when BOTH sides stated something, and the number is the weighted mean of the fields that were measured. Each field carries its points — its share of the score — and the twelve sum to the score exactly. The rest are returned as 'not compared' with the reason, which is usually something the student can add. No model runs at scoring time: every table is in the source.

Public : The student, about themselves: the ranked job board, the role detail page, the saved-search alert emails, the new-offer alert, the daily agent and the roles-for-you feed. No employer sees the number. On these surfaces no MatchExplanation record is written — the score is recomputed on every request from the profile as it stands. Since 2026-09-14 the student's own career service can see this same scorer's number, computed on public projects only, through Institutional Job Match (university-match-student-v3.0.0), which does write records.

Données utilisées, pondérations, supervision et droits

Données d'entrée utilisées

  • Skills (35) — from Job.requiredSkills ∪ Job.hardSkills against the student's canonicalised skill evidence: project skills and technologies (shared projects included), CV skills (User.skills), the competencies of courses linked to their projects, and the degree title (lib/jobs/match-score). Share of the posting's list the student can evidence. Unmeasured when the student has no evidence or the posting lists no skills.
  • Field of study (12) — from disciplinesFromDegree(User.degree) ∪ the disciplines of the student's projects, against Job.targetDisciplines or — for the untagged majority of the board — a keyword classification of the job TITLE (lib/study-closeness disciplinesFromJobTitle). 100 on overlap, 25 otherwise; unmeasured when either side is unknown.
  • Level (10) — from User.programLevel against Job.seniority (derived from title, contract and experience line) and Job.education free text, through lib/study-closeness levelFit. fits 100 / stretch 55 / below 20; unmeasured when either side is unknown.
  • Location (10) — from Job.remoteOk / Job.workLocation, Job.city, Job.region, Job.country (falling back to parsing Job.location) against User.city or the city part of User.location, the country behind User.country, User.preferredLocations (parsed through the same place parser the postings went through), User.willingToRelocate and User.willingToRelocateAbroad. Remote 100; same city or a preferred place 100; same country 75 if willing to relocate else 40; abroad 65 if willing to move abroad else 15. The student's side counts as stated only when they gave a city, a location or a preferred place — User.country defaults to 'IT' and is not read as a statement on its own.
  • Work mode (5) — from Job.workLocation and Job.remoteOk against a remote preference (fitProfile.geographies contains 'remote', or cultureFit contains 'remote-first') or an office preference (cultureFit contains 'office-first'). match 100 / hybrid 60 / mismatch 25; unmeasured when the posting says UNSPECIFIED or the student stated no preference.
  • Contract (8) — from Job.jobType against fitProfile.positionTypes: intern and thesis-stage ask for INTERNSHIP; the individual-contributor, lead, PM, designer and founding types for FULL_TIME, APPRENTICESHIP, CONTRACT or PART_TIME; rotational for FULL_TIME or APPRENTICESHIP; research for any. match 100 / mismatch 30; unmeasured without a stated position type.
  • Languages (8) — from Job.languages (the extractor's canonical names, with a CEFR level when the ad states one) against LanguageProficiency rows: the best of the five CEFR columns, NATIVE when motherTongue. Per required language 100 when held at or above the asked level (or no level asked), 60 when held below or without a level, 0 when missing; the field is the mean. Unmeasured when the posting asks for none or the student listed none.
  • Industry (5) — from Job.companyIndustry (or the recruiter's roleOffering.industry) against User.preferredSectors ∪ fitProfile.industries, case-insensitive substring either way. 100 / 20; unmeasured without a preference or without a stated industry.
  • Company size (2) — from roleOffering.companyStage, else Job.companySize parsed as a headcount RANGE by its upper bound ('11-50' → scale-up; '500+' by its floor), against fitProfile.companySizes. 100 / 20; unmeasured when either side is missing.
  • Motivation (3) — from fitProfile.motivations against the posting's motivations (recruiter roleOffering tags merged with model-extracted Job.extractedSignals, recruiter first), as the share of the student's motivations the role offers (lib/fit-score-engine softAxes). Unmeasured when either list is empty — and so in the eight-axis engine since 2026-10-06, which until then counted a neutral 50 there.
  • Culture (2) — from fitProfile.cultureFit against roleOffering.cultureTags ∪ environment merged with extracted culture tags, same overlap rule. Unmeasured when either list is empty.
  • Dealbreakers (hard zero) — from fitProfile.dealBreakers, as whole-phrase matches in the posting's growth focus, non-negotiables, culture tags, environment and location text, plus a 'no remote' / 'remote-only' phrase against the posting's stated work mode. Any hit sets the score to 0 and names the rule. A posting whose work mode is UNSPECIFIED cannot trip the remote rule.

Jamais utilisées

  • Gender, ethnicity, religion — not fields in our schema.
  • Nationality and date of birth — optional CV fields held elsewhere; not selected by this scorer. Country is read as the country a student LIVES in (User.country, and only once they have stated a city, location or preferred place), never as citizenship, and no work-authorisation inference is drawn from it.
  • GPA, grades, graduation year, university name — not read. A student at any institution with the same evidence scores the same.
  • Photo, name, email — not read.
  • Salary — Job.salaryMin/Max and the student's expectations are not compared. The board filters on pay; the score does not.
  • The fit profile's goal and scope (the engine's semantic 'intent' axis) — not read. That axis is a model call and this scorer runs over up to 20,000 postings per request without one.
  • Job.preferredSkills — displayed and used by the roles-for-you evidence counts, not scored.
  • Recency — no freshness term; the board marks what is new separately and does not reorder for it.

Pondérations de score

Skills evidenced (share of the posting's list)
max. 35 pts
Field of study
max. 12 pts
Level
max. 10 pts
Location
max. 10 pts
Contract
max. 8 pts
Languages
max. 8 pts
Work mode
max. 5 pts
Industry
max. 5 pts
Motivation
max. 3 pts
Company size
max. 2 pts
Culture
max. 2 pts

The weights sum to 100 but the score is a weighted MEAN over the fields that were measured, renormalised — a listed weight is that field's real share only when all eleven measured, and a student who stated nothing but their skills is scored on skills alone. Two honesty rules decide whether a number is printed at all, and both are in the source (lib/jobs/match-breakdown.ts). `match.score` is null unless at least three fields were measured, or the skills field was and the posting names at least three skills: a percentage built from one stated preference, or from one of one skills, is not a match. `matchScore`, the older key the shipped phone apps draw as the ring and caption 'skills matched', equals match.score only when skills were measured and is null otherwise — so a composite made of location and contract alone is never shown under that caption. A dealbreaker sets both to 0. Every response carries all eleven fields with their weight, whether they were measured, their score and a machine-readable reason with parameters, so the number can always be reconstructed from what is shown. Two known softnesses, stated: the field-of-study rule reads the job TITLE with a small keyword table when the recruiter tagged no discipline, and a title it does not recognise leaves the field unmeasured rather than guessed; and the language rule scores 'you have it but at an unstated level' as 60, which is neither a match nor a miss.

Supervision humaine

None, and none is claimed: this scorer orders a list the student reads about themselves. No employer sees the number, no decision about the student is taken from it, and no MatchExplanation record is written for it. The alert cron, the new-offer alert and the daily agent use it only to choose what to mention to the student — which new postings, or whether an offer a company just published is worth a notice (at or above the job-alert floor, with a real skills or field-of-study overlap, under a weekly cap per student) — and all three honour the student's job-alert switch. The company that published the offer is told nothing about who was told.

Vos droits en tant que personne concernée

  • Every score is shown with its eleven rows — the weight, whether the row was compared, the row's own score and the reason — on the board's breakdown sheet and on the role detail. Rows that were not compared say why, and the reason names what would make them measurable (a city, a language, a fit profile).
  • Nothing is stored: the score is recomputed on each request from the profile as it stands, so changing the profile changes the number immediately, and there is no record to correct. For the same reason it does not appear in the data export.
  • The right to human review under GDPR Art. 22 is not engaged: no decision with legal or similarly significant effect is taken from this ranking, by us or by any employer.

Tests de biais

None. Gender and ethnicity are not fields in our schema, so no parity test across them is possible. Three effects are built into the arithmetic and unmeasured: a student who has not answered the relocation questions sees roles outside their city scored 40 and roles abroad 15, so the default profile favours staying put; the language rule scores a language held without a stated CEFR level at 60, which disadvantages students who listed languages without grading them; and the level rule marks senior roles 'below' (20) for every programme level, which is a statement about the posting more than the student but still orders the board. Location, programme level and languages are on every profile, so score distributions across them could be computed from data we hold — and are not.

Contexte réglementaire

  • EU AI Act Reg. 2024/1689, Annex III §4
  • EU AI Act Art. 86 (right to explanation)
  • GDPR Art. 22

Instruments auxquels nous nous référons, et non des obligations que nous déclarons remplies.

Des questions ou des préoccupations ?

Écrivez à info@in-transparency.com pour une explication, un examen humain ou pour exercer vos droits. Les étudiants connectés peuvent aussi déposer la demande depuis l’explication du match dans leur tableau de bord, où elle est enregistrée avec un numéro de demande et la date à laquelle la réponse est attendue.

Ces fiches sont tenues à la main et décrivent les systèmes de score ci-dessus. Elles ne couvrent pas l'analyse qui lit les fichiers de l'étudiant et ne constituent pas une évaluation de conformité.