Home Thirteen thousand skills
00%

RecSys-HR 2026  /  WorkRB Challenge  /  1st place

Thirteen thousand skills, in order

Read one sentence from a job advert. Now rank every skill in the European taxonomy, all 13,891 of them, by how relevant each one is, on a scale from nonsense to exact. Do it for five datasets at once. This page is the method, the numbers, and the discipline that decided which numbers to believe.

Maksim Silchenko  team thylinao, solo National University of Singapore Final 0.8458 macro nDCG@100 Submission 874278
Scroll

01  /  The problem

A fixed vocabulary of thirteen thousand things

Most retrieval problems search an open corpus. This one searches a closed taxonomy, and that changes what is hard about it.

ESCO is the European Union's catalogue of skills, competences and occupations. Version 1.1 contains exactly 13,891 skill concepts. Each one has a preferred label, a paragraph of description, and on average seven alternative labels. There are 97,441 of those in total, collapsing to 96,684 distinct normalised strings.

The task: given a piece of text about work, score all 13,891 concepts and return them in order. Five datasets are scored and averaged with equal weight. Four of them are extraction: the query is a sentence or a segment of a job advert, and several skills are correct at once. The fifth is normalisation: the query is a short surface form like "MS Excel" and the job is to find its canonical entry.

13,891ESCO concepts
in the target space
286labelled validation
queries, total
1,873test queries
across five datasets
10submissions allowed
for the entire event

Relevance is graded, not binary

This is the design decision that invalidates the obvious approach. Every query–skill pair carries an integer grade from 0 to 4, and the grades are semantic rather than confidence levels:

GradeMeaningExample, for the query "maintain the espresso machine"
4correct, explicitly demonstratedmaintain coffee machines
3strongly relevant, impliedperform equipment maintenance
2adjacent: right area, wrong granularitymaintain kitchen equipment
1plausible, in-domain, not asked forprepare speciality coffees
0unrelated, and every unannotated pairoperate a forklift

The example rows are illustrative, chosen to show what each grade means. The real annotations are the organisers'.

Why this kills the obvious recipe. Grades 1 and 2 are real credit, awarded precisely for surfacing taxonomic neighbours, but they must rank below the exact and implied items. Train a cross-encoder on binary labels and it learns to push every neighbour to a hard zero, throwing that credit away for nothing. I measured it: a MiniLM cross-encoder fine-tuned on binary targets scored −0.0758 against the base it was supposed to improve.

The target space 3D · drag to rotate

4 exact3 implied 2 adjacent1 plausible 0 unrelated

Figure 1. A schematic of the 13,891-concept space around one query, coloured by grade. Warm points carry credit; grey ones do not. The cloud is generated, not measured; it is here to show the shape of the problem, which is that a handful of warm points must be lifted out of a very large cold neighbourhood. Drag to rotate.

One task has no local labels at all

Of the five datasets, TechWolf ships a test split and no validation split. It is one fifth of the final score and 324 of the 1,873 test queries, and there is no way to measure anything about it locally. Every decision that touches it is a bet placed blind.

What could be established was its shape. Its queries have a median length of 13 words and 169 of 324 carry a bullet prefix, which makes it structurally an extraction task. Its exact alternative-label hit rate is 3 out of 324, against 450 out of 450 for the normalisation set, so whatever else it is, it is not normalisation-like. It was treated as Tech/House-like throughout, and that assumption held: it finished at 0.8347, in line with the other extraction tasks.

02  /  The metric

Only the first hundred rows exist

Everything below rank 100 is invisible to the score. Everything at the very top is worth exponentially more than everything just below it.

Scoring is macro-averaged nDCG@100 with exponential gain. Two multipliers decide a system's fate: what a grade is worth, and where it sits.

DCG@100  =  Σi ≤ 100   (2reli − 1)  /  log2(i + 2)      nDCG  =  DCG@100 / IDCG@100
2rel − 1  =  0, 1, 3, 7, 15 The gain ladder. A grade-4 item is worth fifteen times a grade-1 item, not four times. Getting one exact skill right outranks getting five plausible ones right.
1 / log2(i + 2) The positional discount. Rank 1 is worth 1.00, rank 10 is worth 0.29, rank 100 is worth 0.15. Shallow and slow, which is why reranking depth pays off far further down than intuition suggests.

Multiply those together and the shape of the problem falls out: the score is almost entirely decided by whether the two-to-ten items graded 3 or 4 land in the first handful of rows. Everything else is rounding.

Build a ranking, watch the score interactive

nDCG@100
0.000
Lost against ideal
DCG / ideal DCG
0.00
Exact item at rank 1

43 210

Figure 2. One illustrative query's graded items in four different orders; bar height is each row's contribution to DCG. The instructive preset is binary-trained. It places the five exact and implied items in the top five rows perfectly, exactly as the ideal ordering does, and still loses 0.026 of nDCG, purely by ranking the merely-plausible items above the taxonomic neighbours beneath them. That inversion is what a model trained on 0/1 labels learns to do, and it is invisible if you only look at whether the top of the list is right. The numbers here belong to this toy query, not to the competition.

A trap in the local scorer. The benchmark library's default summary aggregates hierarchically by task group. Normalisation is its own group while the four extraction tasks share one, so the default number weights the normalisation task at roughly 50% instead of the flat 20% the leaderboard uses. Optimising the default optimises the wrong objective. I computed the flat mean explicitly and later confirmed the leaderboard does the same, across all nine of my own placed rows, to within 0.0001, which is what four-decimal display rounding permits, the largest single discrepancy being 0.00006.

One more property matters, and it saved a lot of wasted effort: nDCG is order-only. Any strictly increasing transform of the scores leaves it unchanged. Calibrating a single final ranker is therefore a no-op. Calibration matters only where scores from different sources have to be made commensurable, which is why every retrieval lane below enters as a per-query z-score rather than a raw similarity.

03  /  The system

Retrieve, featurise, rerank, fuse

Four stages. The expensive one is third, and it is not where the largest gains came from.

The cascade 3D · drag to rotate

Figure 3. 13,891 concepts narrow to a 1,000-candidate pool, which is scored by an ensemble of cross-encoders, fused, and cut to the 100 rows that the metric can see. Depth is a design variable: measured on a fixed reranker, top-100 scores 0.6852, top-200 scores 0.7226 and top-500 scores 0.7393; the later export confirmed the extrapolation, with depth 1,000 beating depth 500 by 0.0029 on lanes plus the cross-encoder alone and by 0.0073 once the prior and the override were added. Candidates and features are therefore exported at 1,000, from which any shallower depth is free.

Stage 1: twelve lanes, two of which do the retrieving

Twelve retrieval lanes score every concept for every query: four domain bi-encoders, two general ones, four lexical, and two string-similarity lanes. The candidate pool comes from just two of them, fused by z-score:

base  =  0.6 · z(CurriculumMatch)  +  0.4 · z(ConTeXT) → top 1000 per query

Two things about that line are worth more than they look. The weights were measured, not guessed, and naive equal-weight reciprocal-rank fusion of the three obvious lanes was worse than its best single component, 0.5935 against 0.6416. Averaging rankers is not free. And the fused base reproduces to 0.6499 exactly, which was wired in as a known-value gate: if the pipeline drifts, the run aborts rather than silently producing a worse submission.

The finding I did not expect. Every lane other than those two is weak on its own: between 0.095 and 0.53 macro nDCG for the nine with a measured macro, against 0.6416 for the best single retriever. All ten were cut as rankers. As features they are a different story, and the lanes that score against the ESCO description rather than the title dominate feature importance despite being among the weakest standalone. Standalone quality turns out to be a poor guide to whether a signal belongs in a fusion. The clearest single case is curricdesc, which ranked top-4 in feature importance while scoring worse standalone than the plain CurriculumMatch lane it was derived from. The same principle shapes the final ensemble, with an important qualification, below.

Stage 2: thirty-three columns per candidate

Each lane contributes two columns: the per-query z-score, and the log of its within-query rank. Nine aggregate columns follow: the base score and its rank, how many lanes place the candidate in their own top 50 and top 200, the mean, spread and max of the lane z-scores, and the two word counts. Twenty-four plus nine is thirty-three.

The agreement and spread columns are the interesting ones. They let the final model distinguish a candidate that every lane likes moderately from one that a single lane loves.

04  /  The cheap levers

The two largest gains that needed no GPU

Three interventions that never touched a GPU are worth about a third of the whole climb, and the largest of them is four columns of arithmetic.

A learned ranker over signals I already had  +0.0291

Replacing the hand-weighted fusion with a LambdaRank model over all thirty-three columns moved the score from 0.6499 to 0.6790. Twelve mostly-bad retrievers, combined by a learned model, beat the best of them by more than any of them is worth on its own, and no GPU was involved.

A correction I had to make to my own draft of this page. I first wrote that this step was worth +0.1050 and was the largest jump of the campaign. It is not. The 0.7549 figure I was quoting is a later ladder rung that already contains a trained cross-encoder column, so most of that gain is neural. The GPU-free lane fusion is worth +0.0291. I am leaving the correction visible because attributing a neural gain to a cheap lever is exactly the kind of error a nice narrative produces.

A smoothed relevance prior  +0.0380

Some ESCO concepts are simply relevant more often than others. Counting that is four columns of arithmetic: computed only over the candidate pools of each fold's training queries, leave-one-out on the training rows, so no label from an evaluated query ever touches its own features:

prior_rel = (r + 1.0) / (s + 4.0) P(grade ≥ 1) prior_hi = (h + 0.5) / (s + 8.0) P(grade ≥ 3) prior_logseen = log(1 + s) how often it was even a candidate prior_egain = (g + 1.0) / (s + 4.0) E[2y − 1], the metric's own gain

In the submitted model prior_hi is the highest-importance feature of all, and prior_egain is fourth, behind two of the cross-encoder log-rank columns. Four columns of counting statistics outrank a four-billion-parameter reranker's score. The effect measured +0.0380 over seven disjoint seeds (figure 4 credits the prior with +0.0331 instead, because that is the ladder rung rather than the isolated lever, and I quote each where it was taken), survived a leave-one-task-out control at +0.0315, and collapsed entirely when the labels were shuffled, which is the control that matters, because a prior computed from the pool is exactly the kind of feature that can leak if you build it carelessly.

An exact-match override  +0.0152 local

If a query string matches an ESCO alternative label exactly after normalisation, that concept is forced to rank 1 at write time, not offered as a feature for the model to weigh, but overridden outright. ESCO carries 97,441 alternative labels, collapsing to 96,684 distinct normalised strings, of which only 0.74% are ambiguous, with a maximum fan-out of four. Two separate measurements describe its effect, and they should not be run together. In isolation the override is worth +0.0564 on the normalisation task and exactly 0.0000 on each of the other three, which averages to +0.0141 over the four local tasks and projects to +0.0113 over the five-task board. Measured instead as a rung of the pipeline ladder, with the prior already in place, it moved the four-task macro by +0.0152, the difference being the interaction with everything beneath it. It was never isolated on the board itself, since no two submissions differed only by this switch. The asymmetry is the whole story: 450 of 450 normalisation queries hit an exact alternative label, against 3 of 324 TechWolf queries.

The distinction that made it work. The same signal offered as a soft feature (substring containment, handed to the model to weigh) was measured and killed: 0.8616 against a 0.8620 baseline. A hard override and a soft feature are not the same intervention. When you know the answer, telling the model about it is weaker than writing it down.

Where the score came from share of the total gain

Figure 4. Every step from the fused base at 0.6499 to the final ensemble at 0.8647, drawn as a share of the total gain rather than of the total score, so the small steps stay legible. Green segments needed no GPU and are worth about a third of the whole climb. For scale, the entire extended competition phase (weeks of dense reranker training on cluster GPUs) moved the score by +0.0011, which is a under a sixtieth of the width of the rightmost segment and would not be visible here.

05  /  The ensemble

The strongest reranker I trained is not in the final system

It was absorbed. What the ensemble was short of was not strength.

Eleven cross-encoders were trained, across two architecture families, each in two stages: a synthetic pre-train on a subsample of a 138,260-row generated corpus (80,000 rows for most members, mixed with up to 14,000 ESCO description-to-skill rows that supply the short-phrase supervision the sentences lack) with a mined negative ladder, then a fine-tune on the real graded annotations inside the candidate pool. All of them train under LambdaLoss with the NDCGLoss2++ scheme, a listwise objective that weights each pair by how much swapping it would change nDCG. When the metric is nDCG and the labels are graded, that is simply the matching objective.

KeyBackboneAdaptationAlone (OOF)In final?
Abge-reranker-base (278M)full0.7327
A2bge-reranker-base, denserfull0.7490
A4bge-reranker-base, 160×48full0.7666
B2bge-reranker-v2-m3 (568M)full0.7955
C2DeBERTa-v3-large, 40×32full0.8161yes
C3DeBERTa-v3-large, 80×32full0.8212
C4DeBERTa-v3-large, 160×32full0.8245yes
QQwen3-Reranker-4BLoRA r=160.8334
Q2Qwen3-Reranker-4B, 40×32LoRA r=160.8375yes
Q8Qwen3-Reranker-8B, 16×24LoRA r=160.8358yes
Q8DQwen3-Reranker-8B, 40×32LoRA r=160.8407no

n×m is the stage-2 density: n listwise rows sampled per query, m documents per row. Q8's figure is derived rather than logged directly, since the ledger records Q8D as +0.0049 over it at the column level. Highlighted rows are the four in the submitted ensemble.

Look at the last two rows. Q8D was the strongest single reranker I ever measured, and every ensemble containing it failed the adoption gate: the pool already held two Qwen members, and its contribution was absorbed. Meanwhile C4 passed. It scores 0.8245 alone, above both DeBERTa columns already in the pool and below all four Qwen-family columns, and it entered by replacing C3 rather than lengthening the ensemble.

But decorrelation is necessary, not sufficient. The campaign measured the counterexample too. ELECTRA-large was the most lineage-decorrelated candidate available (it shares no pre-training with either family) and it failed in all three configurations it was tried in, including one at −0.0021 with zero seeds won. A member has to be both different and good enough that its differences are signal. Q8D was good and not different; ELECTRA was different and not good enough.

Absorbed, or orthogonal 3D · drag to rotate

Figure 5. Ensemble members as directions in a schematic score space. A candidate that points where the ensemble already points adds nothing however long its arrow is; a shorter arrow pointing somewhere new adds a genuine component. This is a picture of an argument, not of measured correlations, but the argument is what the ablation ledger says happened.

One implementation detail worth stealing

Qwen3-Reranker ships as a causal language model that answers a yes/no question. Loading it through the usual sequence-classification path attaches a randomly initialised head and discards everything the checkpoint knows about reranking. Instead, score with the difference of two rows of the existing output embedding:

# h = final hidden state at the last position # the reranker was trained to answer "yes" or "no", so read that directly score = h @ (W_lm["yes"] − W_lm["no"])

Verified to 2.4e-8 against evaluating the full logit vector, and it turns a 151,000-way softmax into a single dot product. The resulting scalar head then trains under LoRA. A different checkpoint routed through the generic path earlier in the campaign produced exactly the random-head failure this avoids, and was cut.

A root cause I got wrong, kept here on purpose. The training code drops queries with no grade-2-or-better item inside the candidate set, and a comment in it attributes four diverged local runs to that fix, the reasoning being that a listwise row with all-identical labels has no defined ideal ranking and produces NaN under LambdaLoss. That attribution is wrong. A later direct measurement refuted it outright: zero of 1,144 and zero of 2,860 sampled rows had all-identical labels, so the ideal DCG was never zero. The divergences tracked step count instead, and their real cause was never established. The guard stays in as defensive coding, but it did not fix what the comment claims. A plausible root cause written confidently into a code comment is exactly the kind of thing that survives unchecked into a write-up.

06  /  The protocol

When the improvements are smaller than the noise

This is the part I think actually decided the outcome, and the part I would most like other people to argue with.

Consider the position after the ensemble reached about 0.864 locally. The remaining candidate improvements were worth +0.001 to +0.002. The validation set is 286 queries. The normalisation task alone carries roughly ±0.03 of sampling noise, and the paired minimum detectable effect on its 50 queries, at 80% power, is ±0.015 to ±0.017. Dozens of plausible configurations remained unmeasured. Ten submissions existed in total, each returning one number, hours later.

The default workflow is a machine for inventing improvements

Try things, keep whatever scores higher locally, submit the best. Under these conditions that is not merely inefficient. It is actively harmful. With enough correlated candidate configurations, something will always look better by +0.002. If you adopt it, you have not improved your system; you have moved it to a slightly worse place and lost the ability to tell.

Worse, the error compounds. Every subsequent comparison is made against a baseline that is itself a lucky draw, so the next real improvement has to overcome an inflated incumbent before it can be seen at all.

Pre-registration

Every round was written into an append-only ledger before the runs existed, enumerating the exact configurations to be tried and closing the space: no configurations outside these templates without a fresh registration line written before the run. Entries are appended, never rewritten; a correction is a new entry.

This is not bookkeeping. It is what makes the denominator of the multiple-comparisons problem knowable. A procedure that allows configurations invented after seeing results has no denominator, and therefore no defensible claim about significance, however it is analysed afterwards.

Paired seeds, on identical folds

Fold assignment is deterministic in the seed and the query count, so a per-seed baseline stored during an earlier round pairs exactly with a candidate measured later. Every comparison runs over seeds 0–9 and reports the paired mean difference, the number of seeds won, and the spread. Pairing validity was checked rather than assumed: before one round, three configurations had to reproduce the earlier harness seed for seed, and parity was recorded as exact to 0.000000 on all three checks.

The gate

Δpaired > +0.0010   AND   seeds won ≥ 8/10   AND   sd ≤ 0.0015

The moment that makes the rest of it credible. In the final round, configuration c4b returned Δ = +0.0010 and won ten seeds out of ten. It was recorded as a failure, on the strict inequality. The registration had pre-committed: near-miss = fail, no exceptions, no post-hoc re-argument. A gate that bends once is not a gate, and every other number on this page would be worth less if that one had bent.

The multiplicity guard, and the winner's curse

Anything that passed faced a confirmation run on fresh seeds 10–19, keeping its pass only if it beat the incumbent there too. This was exercised exactly once, on the only passer of the extended phase. Its discovery estimate was 0.8647; the pooled twenty-seed estimate was 0.8644. The direction is what the winner's curse predicts and the size matches the 0.0003 haircut registered for it in advance, but against a measured across-seed spread of 0.0007, a single 0.0003 gap is well inside noise. Read it as consistent with the curse, not as a measurement of it.

When selecting among many correlated subsets (one round evaluated twenty-six) a haircut of 0.0003 was subtracted from the winner before comparing it to the decision threshold, sized to the selection breadth.

Why pairing is not optional interactive

Unpaired verdict
Paired verdict
Seeds won
Gate
Figure 6. Two systems differing by the effect size you set, evaluated on the same seeds. Grey dots are the two absolute scores per seed; they scatter far more than the effect. Coral bars are the per-seed difference, which is what pairing recovers. At the true competition effect size the unpaired comparison is hopeless and the paired one is decisive, on identical data. Drag the sliders.

The slot bar: turning a local score into a probability

The last question is when a locally better configuration is worth one of ten irreplaceable submissions. That needs a mapping from local score to leaderboard score, and the mapping is empirical, but it moves, which is the first thing to say about it. Measured seven times across the campaign, it ran from −0.0086 for the first fitted submission to −0.0235 for a mature one. A fitted system transfers worse than a zero-shot one, and the offset roughly doubled as the system grew. The bars below rest on the four most recent own-row offsets only, which do concentrate: mean −0.02005, standard deviation 0.00077.

Two of those seven were free. Two zero-shot baselines already sat on the public leaderboard, and I could score them locally. That gives an offset estimate on day one, without spending a submission. That generalises to any competition with a public baseline row.

And its limit. Those two free points read −0.0124 and −0.0139, while the mature pipeline settled near −0.020. A zero-shot baseline transfers better than a fitted system does, so the free estimate is optimistic and has to be superseded once real rows exist. It is a starting prior, not a substitute for your own measurements.

Given a target board score, the local score needed to beat it with probability p follows from a one-sided prediction interval on a new offset draw. With four offsets in the final estimate that is a t-interval on three degrees of freedom, not a normal z-interval, a distinction a reviewer caught, and one that moved the thresholds enough to matter:

Candidate familylocal for ~50%for ~90%
Blend family0.86540.8668
Lean single-config family0.86400.8654
Unconditional0.86470.8661

These bars were registered before the final round, which made the last decision mechanical rather than a judgement call. The adopted configuration sat at 0.8647 local, clearing the lean-family 50% bar of 0.8640, not the 90% bar, and was submitted on exactly that basis. It scored 0.8458 against the row it needed to beat at 0.8444.

What the protocol cost

It is only fair to record the price. The protocol rejected c4b, which beat its incumbent on ten seeds out of ten. It rejected three Q2D configurations at +0.0009, +0.0009 and +0.0006 with eight and nine seeds won. It rejected an ESCO attribute join at +0.0005 with 6/10. And it refused a recombination of my own already-scored leaderboard rows worth +0.0002, not excluded by any rule I could point to, though my own review recorded the rules-letter status as ambiguous, since the rules require the submission file to be produced by the real benchmark tasks.

A protocol tuned this conservatively will, in expectation, leave small real gains on the table. The trade is deliberate. When measurement error exceeds effect size, the dominant risk is not missing a real gain; it is adopting a false one and then building on top of it.

07  /  Results

One configuration passed

The complete pre-registered ledger of the final phase. Every row was registered before it ran; every row is here.

HouseTechTechWolfSkillSkapeSkillNormMacro
Submitted system0.83870.83900.83470.86830.84800.8458

Submission 874278. Configuration C2, C4, Q2, Q8 at depth 1,000, prior and override on, bagged over ten seeds. Build cross-validation on three seeds gave 0.8649 / 0.8659 / 0.8652 → 0.8653, so the realised local-to-board offset was −0.0195, inside the pre-registered band of −0.0192 to −0.0199.

The shape of that row. Among the nine accounts above it on the closing public board, it is the only one whose normalisation score exceeds its own extraction average: 0.8480 against 0.8452. Two things produce that sign and only one of them is measured. The alternative-label override lifts normalisation and nothing else. But this system's extraction scores also sat about 0.05 below the top of the board, which lowers the other side of the same comparison. The inversion says as much about where it was weak as about where it was strong.

The final-phase ledger

RoundConfiguration10-seedΔ pairedWinsVerdict
Incumbents, re-derived on the same seeds
A4,B2,C2,C3,Q2,Q80.8641incumbent
C2,C3,Q2,Q8 (lean)0.8637incumbent
blend0.8650incumbent
Blend round 2: 5 configurations, 26 subsets
Blendbest subsets+0.00046–7/10fail
BlendQ,Q2,Q8 (Qwen trio)0.8617fail
BlendA4,B2,Q2,Q8 (no DeBERTa)0.8628fail
BlendA4,B2,C2,C3 (no Qwen)0.8465fail
B-family
B-famA4,B,B2,C2,C3,Q2,Q80.8639−0.00023/10fail
B-famB,C2,C3,Q2,Q80.8638−0.00034/10fail
New feature blocks
Featurecross-encoder dispersion0.8641−0.00004/10fail
FeatureESCO attribute join0.8646+0.00056/10fail
Dense-column rounds
Q8DC2,C3,Q2,Q8D0.8635−0.00024/10fail
Q8DC2,C3,Q2,Q8,Q8D0.8642+0.00058/10fail (mean)
Q8DA4,B2,C2,C3,Q2,Q8D0.8636−0.00053/10fail
EC2,C3,Q2,Q8,E0.8637+0.00014/10fail
E7-column + E0.8637−0.00043/10fail
EE,Q2,Q80.8615−0.00210/10fail
The C4 round
C4c4a: C2,C4,Q2,Q80.8647+0.001110/10pass
C4c4b: C2,C3,C4,Q2,Q80.8646+0.001010/10fail (strict >)
C4c4c: A4,B2,C2,C4,Q2,Q80.8646+0.00057/10fail
Round Q2D and the combined swap
Q2Dq2da+0.00098/10fail
Q2Dq2db+0.00098/10fail
Q2Dq2dc+0.00069/10fail
CombC2,C4,Q2D,Q8D0.8639+0.00025/10fail
Addlean + ESCO attributes0.8636−0.00014/10fail

Spread across seeds was inside the 0.0015 gate for every row where it is reported; the passer measured 0.0007. It then cleared fresh-seed confirmation on seeds 10–19 at +0.0010 with 9/10 wins, and was submitted. Look at the three Q2D rows: +0.0009, +0.0009 and +0.0006 with eight and nine seeds won. Those are precisely the results a permissive protocol adopts and a fixed gate does not.

08  /  Dead ends

Everything that did not work

Each of these cost real time. They are here so that they need not cost anyone else's. Click any card.

Reranking

A zero-shot general-purpose reranker over the best domain retriever's top 200.

−0.061 macro, lost on every task
Training

Fine-tuning a cross-encoder on 0/1 relevance instead of the real 0–4 grades.

−0.0758
Data

Generating extra labelled queries and mixing them into fine-tuning.

−0.0094 at full dose
Taxonomy

The obvious missing signal: parent and child concepts. It could not be obtained.

+0.0000 over flat attributes
Features

Skill type, reuse level, label count, description length. The closest of the feature-block calls.

+0.0005, 6/10 wins, still a fail
Features

One standard-deviation feature across the cross-encoder scores.

0.8641: identical to baseline
Preprocessing

Removing the leading markers from 169 of 324 TechWolf queries. Obviously correct, and probably wrong.

−0.0120 in a smoke test
Tuning

A LightGBM sweep whose winners reversed on disjoint seeds.

Abandoned and locked
Fusion

A LambdaRank model over twelve mostly-weak retrieval lanes.

+0.0291, and no GPU
Features

Four columns of counting statistics over the training folds' candidate pools.

+0.0380, seven disjoint seeds
Rules

Forcing an exact alternative-label match to rank 1 at write time.

+0.0564 on normalisation, 0.0000 elsewhere
Calibration

Scoring someone else's published baseline row locally, instead of spending a submission.

2 of 7 calibration points, 0 submissions

09  /  The channel

What a leaderboard cannot tell you

A fact about data hosting, and a negative result about detection that I reached only after being confidently wrong about it first.

What this section is not. It concerns how the datasets were hosted, and a methodological result about leaderboard statistics. It makes no claim about any participant, names none, and deliberately withholds the operational detail that would let a reader reconstruct a shortlist. It is here because the organisers asked for insights that would help a future edition.

The channel

The five graded datasets are published on the Hugging Face Hub. Their qrels/test configurations were served, unauthenticated, through the public datasets-server API. One request returns their sizes:

curl "https://datasets-server.huggingface.co/size?dataset=TechWolf%2FSkill-extraction-House-graded&config=qrels"

I have two point observations, not continuous coverage. On 30 July, five days before the test phase closed, all five graded repositories exposed public qrels/test and queries/test splits whose counts matched the competition's exactly. On 4 August at 01:08 UTC, an hour after close, I re-pulled and recorded the sizes: 528, 583, 582, 1265, 450 qrels rows against 261, 338, 324, 500, 450 test queries. So the splits were reachable during the competition and remained so after it, though I did not monitor the endpoint continuously and cannot say they were reachable throughout.

The datasets' own cards document the situation candidly: the published test labels are "real but binary" and "derived from the public non-graded ground truths", with the graded 0–4 annotations "withheld during the ongoing RecSys-HR challenge". So the withheld object was genuinely withheld. What stayed reachable was a binary relevance judgement over the exact test queries. The rules prohibit enriching training with test-source data, which makes this a rules question rather than a grader vulnerability. I reported it to the organisers before results and the summary paper were finalised, and asked explicitly that my own submissions be included in whatever check followed.

The lesson for a future edition. A test set is not withheld because the interesting column has been dropped from a published table. It is withheld when the rows themselves are unreachable. Gating the qrels splits for the duration of a competition costs nothing and removes the question entirely.

Can a leaderboard reveal that such a channel was used?

I spent real effort on this question and got it wrong, so the wrong answers are what this section reports.

The first statistic. Cross-sectional: for each row, take the mean extraction score minus the normalisation score, and treat a large positive gap as anomalous. The reasoning is that a source covering the extraction tasks would lift them and leave normalisation behind. One number per row, nothing needed but the public board, and on a short list of rows it appears to separate them cleanly.

It fires on a majority of the board. Computed over every row, a majority have a positive gap. A statistic that flags more than half the population is not detecting an anomaly; it is measuring a property of the tasks. Extraction and normalisation have different attainable ceilings and most systems are better at the first. Withdrawn.

The second statistic. A within-account step: between consecutive submissions by one account, does the subset of tasks a source would help move together and sharply while the rest stays flat? This at least controls for the system, which the cross-sectional version does not.

It is confounded by the most ordinary act in competitive machine learning. A task column that does not move between two submissions usually means nothing more than that a per-task prediction file was reused unchanged, a routine thing to do while iterating on one task. On this board that pattern occurs in roughly one consecutive pair in seven, across accounts spanning the full range of scores, including on submissions whose own descriptions identify them as reruns of the same baseline. A pattern with that base rate carries little evidential weight.

I am also not publishing the thresholds. The leaderboard is public and the statistic is deterministic, so exact values would hand any reader a reproducible shortlist of accounts, which is precisely the accusation this page declines to make, merely routed through the reader.

Why a base-rate check kills both schematic

Figure 7. Schematic. Left: what a statistic looks like when you evaluate it on the short list you were already looking at: clean separation. Right: the same statistic evaluated on the whole population, where it fires on a majority and the separation disappears. The picture is generic; it is the shape of the mistake, not a plot of any particular board.

Where this ends up

I do not think any pattern in the public scores can distinguish a compliant system from a non-compliant one here. I was wrong to think otherwise.

Rules compliance is a question for organisers with access to submissions and code, which is where I left it.

Two things produced that reversal, and both are cheap enough that anyone attempting this kind of analysis should run them first.

The base-rate check. How often does the pattern fire across the whole population, rather than across the short list you were already looking at? Both statistics above survive scrutiny on a handful of rows and die on the full board.

The reference-class check, which is the uncomfortable one and the one I would emphasise. My own entry was the clearest outlier on the cross-sectional statistic: all nine of my placed rows fall on the opposite side of it. Which means the reference class I was implicitly comparing against was one I had constructed by removing everyone whose system did not resemble mine. A statistic that flags "systems unlike mine" will always find some, and it will always feel like a discovery.

On the adversarial review. An earlier and considerably more accusatory version of this analysis went through a review in which independent reviewers were instructed to refute each claim, with refutation as the default under uncertainty. Of 72 claims examined, 14 survived. The detail worth recording is that the cross-sectional statistic was not among the claims that review rejected. It survived, and I withdrew it myself afterwards when the base-rate check destroyed its interpretation. A claim can pass adversarial review and still be wrong. That is an argument for running the cheap population-level check in addition to the expensive argument-level one, not instead of it.

10  /  Lessons

What I would tell myself in May

Ranked by contribution per unit of effort, the levers invert almost exactly the order in which one naturally attempts them.

≈+0.138Cross-encoders
(all the GPU time)
+0.0331Counting statistics
(the prior), no GPU
+0.0291Learning to rank
over lanes, no GPU
+0.0152Exact matching
in the right place, no GPU
+0.0011The entire
extended phase

Sequence cheap levers first, and not only because they are cheap

The three no-GPU levers total about +0.077: roughly a third of the gain over the fused base, for a rounding error of the cost. That is not an argument against cross-encoders: +0.138 was decisive for this entry, and I know of no configuration of the cheap levers alone that came close. Whether the top of this board was reachable without them I cannot say, since I have no visibility into any other entry's architecture beyond a one-line self-description.

It is an argument about ordering, and the criticism lands on me. The cheap levers were available from day one and I did not reach the prior until the campaign was two-thirds gone. They also had the largest effects relative to their measurement error, which made them the ones a 286-query validation set could actually verify. By the time the campaign reached effects of +0.001, measurement had become more expensive than training.

Ensembles want decorrelation, not strength

The strongest reranker I trained is not in the final system, because what it knew was already there. But ELECTRA (the most decorrelated candidate available) failed too, and worse. A member has to be both different and good enough that its differences are signal. Standalone quality is a poor guide on its own; so is novelty on its own.

This is not new in the ensembling literature. It is worth restating in a competition context because the instinct under time pressure is always to train a bigger version of whatever currently scores highest.

Treat the submission quota as the scarce resource it is

Ten submissions, each returning one scalar hours later, is a bandit problem with an expensive arm. Two of my seven calibration points came free, from locally scoring baselines someone else had already put on the board. That trick generalises to any competition with a public baseline row.

Write down what you are going to do before you do it

Everything above rests on an append-only ledger that recorded each round's exact configuration list before the round existed. It is the least glamorous part of the campaign and the one I would keep if I could keep only one. Without it, the last two weeks would have been a search over noise, and I would not have been able to tell.

On the final standing. At the close of the test phase the public board carried 98 rows from 29 accounts, and my best row ranked tenth on score. The organisers then verified rule compliance across entries (a process they described as taking considerably longer than expected) and the final standing announced afterwards placed this entry first. I have no visibility into that process, and this page does not speculate about the compliance status of any other entry. I make no claim that the protocol described in section 6 is why the entry finished first; on score it did not separate itself from anything. What the protocol did was make the numbers on this page reportable.