Spider 2.0-Snow: what makes an instance hard?

Interim findings from an all-pairs execution study of the vxef/spider2-snow-temperature-sweep rollouts — Qwen3 {1.7B, 4B, 8B} × {thinking, no-thinking}, 100 samples per instance per cell, 523 grounded instances. Every distinct generated SQL was quoting-normalized (identifiers quoted, case preserved), compiled with Snowflake EXPLAIN, and every compile survivor was executed and scored against gold (Spider 2's own comparison); the raw as-is SQL got its own EXPLAIN sweep. All records are in the dataset PR and browsable row-by-row in the explorer (quoted-verdict filter + expected-vs-got diffs). July 16, 2026.

1 · pass@k by model and reasoning arm

The topline: mean pass@1 across instances, per cell (each cell = one model size × one reasoning arm, 100 quoting-normalized samples per instance; error bars are 95% bootstrap CIs over instances). Thinking roughly doubles-to-triples accuracy at every size — 4B-think (4.3%) beats 8B-no-think (2.8%), so the reasoning arm is worth more than a size doubling here — and the best cell, 8B-think, still sits at 6.9%. The right chart shows what a sampling budget buys: the expected share of instances solved at k samples (mean unbiased pass@k); at k = 100 the best cell reaches ~22% of instances, and the curves are still rising — sampling is nowhere near exhausted on the solvable set.

2 · The funnel: two walls, not one

Reading each cell's samples left to right: finish → parse (sqlglot) → static grounding → compile (Snowflake EXPLAIN) → ranmatched gold (≡ pass@1). The compile stage is not another static check: EXPLAIN is a dry run through Snowflake's actual query compiler against the live account catalog — parsing in the real dialect, resolving every table/column name under the engine's quoting and case-folding rules, type-checking expressions and function signatures, checking privileges — everything short of dispatching the plan for execution (it runs in the cloud-services layer, no warehouse needed). That makes it strictly stronger than client-side static analysis, and weaker than execution only on runtime, data-dependent failures (division by zero, casts on bad values, timeouts), which the “ran” stage then catches. The “compiles (as-is)” group is the raw SQL's compile rate — formatting alone (unquoted case-sensitive identifiers) kills 97–99% of every cell's raw queries before anything else can matter; the adjacent “compiles (quoted)” group shows the same queries after quoting normalization. What remains of the compile wall is semantic. The bar chart below is a failure-mode census of the 142k distinct failing (instance, SQL) pairs — the union across cells, deduped because identical SQL compiles once; it characterizes the corpus of errors, not any one model: 43% reference a nonexistent column and 29% a nonexistent table — schema-linking failures, not dialect (5%) or syntax (3%). Browse compile failures →

3 · Per-instance pass rates — a consistent frontier

Each cell's instances ranked by pass@1 (that cell's 100 samples; only instances with ≥1 pass drawn — the counts at zero are in the hover). Each point is an instance; click to open it in the explorer. (We deliberately don't plot pass@1 against pass@k for the same pool — both are deterministic functions of the same pass count, so that “relationship” is a parametric curve with no information beyond this ranking.) The solvable sets are nested: 90–100% of every weaker cell's solvable instances are contained in 8B-think's 114 (per-instance pass@1 Spearman 0.83 between 4B-think and 8B-think) — capability growth widens one frontier, it does not unlock different problems. Per-instance whiskers are 95% bootstrap CIs from resampling that instance's own 100 samples with replacement (equivalently c* ~ Binomial(100, c/100)); lower bounds of 0 are truncated at the axis floor, and CIs this near c = 0 are rough by nature.

cellsolvable instances (pass@100 = 1)contained in 8B-think's set
Qwen3-1.7B · nothink99 (100%)
Qwen3-1.7B · think3534 (97%)
Qwen3-4B · nothink1918 (95%)
Qwen3-4B · think9686 (90%)
Qwen3-8B · nothink4140 (98%)
Qwen3-8B · think114114 (—)

4 · What predicts hardness (and what doesn't)

Mean pass@k across instances per covariate bin, per cell; error bars are 95% bootstrap CIs resampling instances within bin. The cell's own compile rate is the strongest predictor (schema-linking again; solvable-vs-not AUC 0.86 in the 8B-think cell). Self-inconsistency (distinct-SQL rate) is the best behavioral tell. Question length acts as a threshold, not a slope. Static column-grounding — the flag the dataset ships — is ~uninformative (AUC 0.51, 8B-think). Thinking-token volume predicts nothing for 4B/8B; explicit date/window phrasing drops match-given-compiled from 0.23 to 0.11 (8B-think; same sign in 4B-think). Beyond compiling, misses are far off, not near-misses — a census of the ~30k distinct ran-but-wrong executions (union across cells): 31% have exactly the gold's shape with wrong values, and unit/rounding confusions are absent (1 case of ×100 in 3,355 scalar comparisons).

5 · The tantalizing stratum: compiles often, never matches

177 instances (8B-think) have ≥10 compiled samples and zero matches — the analytic-composition wall. They read like multi-step aggregation with precise windows and exact output contracts. This is the stratum where execution-feedback methods have signal to amplify; the never-compiles stratum needs grounding (constrained decoding over schema identifiers) instead.

instancedbcompiled /100question
sf_bq350OPEN_TARGETS_PLATFORM_196For the detailed molecule data, Please display the drug id, drug type and withdrawal status for approved drugs with a black box warning and known drug type among 'Keytruda', 'Vioxx', 'Premarin', and 'Humira'explorer →
sf_local037BRAZILIAN_E_COMMERCE94Identify the top three product categories whose most commonly used payment type has the highest number of payments across all categories, and specify the number of payments made in each category using that payment type.explorer →
sf_local097DB_IMDB92Could you analyze our data and identify which ten-year period starting from any movie release year present in the data had the largest number of films, considering consecutive ten-year periods beginning at each unique year? Only output the start year and the texplorer →
sf_bq230USDA_NASS_AGRICULTURE91Using the crops dataset, find the total 2022 production figures, measured in bushels, for corn from the 'FIELD CROPS' category and mushrooms from the 'HORTICULTURE' group for each U.S. state. Only include data rows where 'statisticcat_desc' is 'PRODUCTION', 'aexplorer →
sf_bq130COVID19_NYT89Analyze daily new COVID-19 case counts from March to May 2020, identifying the top five states by daily increases. Please compile a ranking based on how often each state appears in these daily top fives. Then, examine the state that ranks fourth overall and idexplorer →
sf_bq307STACKOVERFLOW87Find the top 10 gold badges that users most commonly earn as their first gold badge on Stack Overflow. For each of these badges, display the badge name, the number of users who earned it as their first gold badge, and the average number of days from the user'sexplorer →
sf_local059EDUCATION_BUSINESS84For the calendar year 2021, what is the overall average quantity sold of the top three best-selling hardware products (by total quantity sold) in each division?explorer →
sf_local019WWE84For the NXT title that had the shortest match (excluding titles with "title change"), what were the names of the two wrestlers involved?explorer →
sf_local034BRAZILIAN_E_COMMERCE82Could you help me calculate the average of the total number of payments made using the most preferred payment method for each product category, where the most preferred payment method in a category is the one with the highest number of payments?explorer →
sf_bq028DEPS_DEV_V181Considering only the latest release versions of NPM package, which packages are the top 8 most popular based on the Github star number, as well as their versions?explorer →
sf_bq396NHTSA_TRAFFIC_FATALITIES81Which top 3 states had the largest differences in the number of traffic accidents between rainy and clear weather during weekends in 2016? Please also provide the respective differences for each state.explorer →
sf_local358LOG81How many users are there in each age category (20s, 30s, 40s, 50s, and others)?explorer →
Method notes: eval scope was 202,532 distinct raw SQLs EXPLAINed, 182,583 distinct quoted forms, 40,580 compile survivors executed & scored — unique (instance, SQL) pairs, deduped across the six cells because identical SQL executes once (corpus counts, not performance numbers). pass@k, precisely: an instance has n = 100 samples, of which c pass. pass@k is the probability that a batch of k samples, drawn uniformly without replacement from those n, contains at least one pass — equivalently, the long-run average over repeatedly drawn batches (batches with replacement, the k samples within a batch without). Writing C(a,b) for the binomial coefficient (“a choose b”, the number of ways to pick b items from a), there are C(n,k) possible batches of which C(n−c,k) contain only failures, so pass@k = 1 − C(n−c,k)/C(n,k). We compute this exactly rather than by Monte Carlo; it is the standard unbiased estimator of true pass@k under i.i.d. sampling (Chen et al. 2021). The quoting treatment itself, quantified across the 214,542 distinct raw (instance, SQL) pairs holding verdicts in both arms: quoting rescues 19.2% (raw compile-fail → quoted OK) and harms 0.3% (616 pairs where the raw SQL compiles via case-folding of a written-lowercase name onto an uppercase-stored object, and preserving the written case inside quotes breaks the match) — a 67:1 benefit ratio. “Compile” treats executed pairs as compiled unless execution hit a SQL compilation error. Executions used a 30s statement timeout, partly on an X-Small and partly on a MEDIUM warehouse (resized mid-run); results >25k rows were scored 0 without comparison (note field, auditable). Quoting normalization is a modified-SQL diagnostic — raw as-is results are the firstpass columns. Full method & caveats: dataset PR README.

6 · Oracle identifier repair: is the cliff about names?

The funnel (section 2) shows a large share of rollouts failing at the ungrounded-identifier stage — a wrong table or column name. It's tempting to read that as the fix: ground the identifiers and the query passes. Generally, it doesn't: even with an oracle that repairs every identifier, these queries would still fail, for other reasons — and where repair does flip the outcome, it's mostly the mirage channel, converting a visible compile error into a query that runs and returns the wrong answer, not into a pass. How do we estimate this, and what kind of failures are left after fixing identifiers? The analyses and worked examples below tell that story.

How we estimate this

We use a two-tier oracle repair. Tier 1 is a deterministic, compile-error-driven fuzzy-match fixer that swaps in the schema-correct table/column name, run over a seeded stratified sample of 300 failing (instance, SQL) pairs drawn across all six model×arm cells. Tier 2 escalates the pairs tier‑1 can't confidently fix to an LLM oracle armed with the ground-truth schema and EXPLAIN/execution feedback, piloted on a small, deliberately biased 10-pair probe weighted toward the census's most-repairable-looking class — enough to exercise the protocol, not to estimate a rate. Both tiers are held to the same rule: an AST-isomorphism gate allows only identifier nodes to change, so what's measured is the ceiling of identifier-only repair, nothing else.

In detail. For a seeded stratified sample of 300 failing (instance, SQL) pairs — drawn across the 6 model×arm cells, split into three strata (cliff1: compiles down to the right tables but not fully grounded, exactly the funnel's second wall (section 2); notab: references a table that doesn't exist at all; control: fully grounded per the dataset's own static flags yet still failed some other way, a negative control) — an oracle repair loop replaces bad table/column identifiers with the schema-correct name (ground-truth SHOW COLUMNS inventory, fuzzy-matched, EXPLAIN/execution feedback) and changes nothing else: every edit is gated by an AST-isomorphism check (only identifier nodes may differ from the original parse skeleton). This measures the ceiling of identifier-level intervention — what a schema-constrained decoding scheme could buy — independent of any model's own capability. Tier 1 (a deterministic, compile-error-driven fuzzy-match loop) processed all 300/300 pairs. Tier 2 (an LLM subagent per pair, same identifier-only contract, allowed judgment on tier-1's escalations) was piloted on 10 pairs and deliberately stopped there — drawn to exercise the protocol, overweighting the census's most-repairable-looking class (intable), not to estimate a rate. Every tier-2 number below is a qualitative read on the escalated error corpus, union across cells — never a per-cell rate, and never pooled with tier-1's own per-cell numbers.

What's left after fixing identifiers

Almost nothing passes, and most of what changes is the mirage channel described above. Past the identifier layer, the remaining failures sort into six recurring classes — a parsing-artifact gap, missing joins, Snowflake dialect gaps that need FLATTEN over nested VARIANT/array fields, invented columns the model wished existed, assorted other-structural issues, and the mirage itself (compiles, runs, wrong answer) — each illustrated with worked examples below.

The headline is a decomposition, not a recovery. Of the 300 sampled pairs (50 drawn from each of the 6 model × arm cells — 30 cliff-1, 10 not-table-grounded, 10 fully-grounded control per cell; no pair appears in more than one cell), tier-1 repair passes exactly 1. 13 already compile with zero identifier changes (noop_compiles — nothing for an identifier-only repair to do, so not an identifier bug); 2 compile and run after a fix but don't match gold (ran_no_match, the mirage channel that inflates section 5's tantalizing stratum); 1 compiles and runs then errors at execution; the remaining 283 escalate — tier-1 can't confidently apply a same-skeleton fix (187 escalate_structural, 56 escalate_no_candidate — no fuzzy match anywhere in scope, 30 escalate_ambiguous — multiple similarly-close candidates, 10 escalate_no_progress). 76 of the 187 escalate_structural rows are a parsing artifact, not a structural one: tier-1's error parser deliberately doesn't handle a dotted/qualified identifier (e.g. invalid identifier '"u"."owner_user_id"'), so those land in escalate_structural by construction. That gap doesn't hide easy wins either — the tier-2 pilot specifically probed 2 of the 76 (sf_bq169, sf_ga001), and a third was examined by hand outside the pilot (sf_local356); none passed.

A static census of cliff-1's 22,662 unique failing pairs (referenced-table candidate pools only — a close match in an unjoined table counts as missing-join evidence, not a typo) shows why so little of this converts to pass: 10.8% are intable-close (every bad column has a ≥0.75 fuzzy match in a table the query already references), 10.5% are elsewhere (a close match exists only in a table never joined), 45.4% are nowhere (no close match anywhere in the schema), and 33.2% are mixed. Even intable-close is dominated by coincidentally-close hallucinated columns, not typos: sf_local356's prev_position → position is the clearest case — the model wants each lap's previous position, which is a LAG() window computation, not a stored column. The models hallucinate the schema they wish existed: flattened, pre-joined, pre-aggregated.

Reweighting the sample's tier-1 outcomes back up to each cell's own cliff-1 population (post-stratification over stratum × static_class buckets: sampling weight = population share ÷ sampled count; a handful of small notab/control buckets some cells' draws never hit are excluded from the denominator as a coverage gap rather than guessed — cliff-1 itself always has full coverage, since sample.py's own quota always fills its 4 classes) gives, per cell, where cliff-1's failing mass ends up. Browse the underlying compile failures →

The two views below — unique pairs vs. rollout mass (each pair weighted by how many of that cell's own 100-per-instance samples produced it) — agree closely: pass stays at zero in 5 of 6 cells and reaches only 1.5% (unique-pair) / 1.4% (rollout) in the one cell it doesn't, 4B·think. Converting that into a pass@1 headroom estimate (repaired-pass rollout mass ÷ that cell's total 100-per-instance sample budget) tops out at 0.04 percentage points — extremely rough, since it rests on a single pass drawn from a 10-pair bucket, but nowhere close to the several points needed to explain the funnel in section 1.

cellcliff1 → pass (unique-pair / rollout-weighted)cliff1 → ran_no_match (unique-pair / rollout-weighted)cliff1 → unfixable (unique-pair / rollout-weighted)pass@1 headroom (ppt)
Qwen3-1.7B · no-think0.00% / 0.00%0.00% / 0.00%100.0% / 100.0%0.000
Qwen3-1.7B · think0.00% / 0.00%1.24% / 2.47%98.8% / 97.5%0.000
Qwen3-4B · no-think0.00% / 0.00%0.00% / 0.00%100.0% / 100.0%0.000
Qwen3-4B · think1.49% / 1.42%0.00% / 0.00%98.5% / 98.6%0.036
Qwen3-8B · no-think0.00% / 0.00%0.00% / 0.00%100.0% / 100.0%0.000
Qwen3-8B · think0.00% / 0.00%0.00% / 0.00%100.0% / 100.0%0.000

Tier-2 pilot (10 of 10 pairs verdicted at build time): 0 PASS, 3 RAN_NO_MATCH, 7 UNFIXABLE (4 other-structural, 2 dialect-needs-flatten, 1 missing-join) — a tally over the escalated error corpus, union across cells, not a per-cell rate (n is far too small for that). The taxonomy below merges these pilot diagnoses with five hand-examined walkthrough pairs into six illustrated failure classes, each linking to the pair's instance in the explorer.

Method notes (repair): population/sample frames, the reweighting formula, and every number's producing pandas expression are in spider2_repair/metrics.py (python -m spider2_repair.metrics --emit-json); hand-recomputed reconciliation checks for two (cell, stratum, static_class) buckets against this reweighted table matched exactly (the reconciliation method is documented in per_cell_tables()'s docstring in spider2_repair/metrics.py). Coverage below 100% for a stratum (never cliff-1, always 100% there) means some static_class bucket in that cell's population was never sampled — a shortfall sample.py itself warns about at draw time — and that bucket's population is excluded from the reweighted percentages rather than guessed. escalate_* means “tier-1 escalated, unresolved by tier-1,” not a proven-unfixable claim; the tier-2 pilot's 10-pair, hand-audited adjudication is the only source of an actual UNFIXABLE/RAN_NO_MATCH/PASS verdict on any of these pairs, and per design it is never projected onto a cell.