id: an-100 hypothesis: shell-contratante headline: Sponsor-blind detection of slanted polls is feasible but imperfect. Within-poll features computed from consensus deviation — signed_spike (max minus 2nd max cand-row deviation), poll_std_dev, mean_abs_dev — distinguish candidate-sponsored from unsponsored polls with AUC ≈ 0.64–0.69. The signed_spike best single feature: 17.2 pp for sponsored vs 10.1 pp for unsponsored, AUC = 0.636. Combined multivariate logit AUC = 0.685. Concrete policy mechanism: a publicly-computable "suspicion score" the TSE / journalists / regulators could use to triage polls for closer audit, requiring no sponsor identity information. type: policy-mechanism question: "Can we detect slanted polls without knowing the sponsor identity, given access to peer polls?" tags: ["hyp:shell-contratante", "policy-mechanism", "detection", "consensus-deviation", "blind-audit"] status: interpreted status_date: 2026-06-17 confidence: green created: 2026-06-17 script: source/analysis/an-100-blind-slant-detection.py target: build/table/an-100-blind-slant-detection.csv
AN-100: Sponsor-blind detection — a policy mechanism
User question (2026-06-17): "We should focus on whether one could detect signs of bias without knowing who is the sponsor. Do we have a good way of detecting that given all this? (assuming sufficient other polls)."
This is the constructive answer: a publicly-computable detection score that does not require sponsor identity.
Method
For each poll p with K_p ≥ 2 candidate-rows, compute:
max_signed_dev= max over cands of (poll_share − consensus)max_abs_dev= max over cands of |poll_share − consensus|signed_spike= max(consensus_dev) − second-max(consensus_dev)poll_std_dev= SD of cand-row deviations within the pollmean_abs_dev= mean |deviation| across cands in the pollskew_dev= skewness of cand-row deviations
Where consensus is the median of OTHER polls of the same
candidate in the same race within ±14 days (AN-099
construction). UNSPONSORED-only pool for the cleanest baseline.
These features are all computable from public TSE data without sponsor information.
Detection logic
A slanted poll boosting candidate X by +7 pp produces:
- X's deviation: ≈ +7 pp
- Other cands' deviations: near zero (or slightly negative)
- → high signed_spike (one big positive, others near zero)
- → high poll_std_dev (wide spread)
- → positive skew_dev (one outlier)
An honest noisy poll has all cand-row deviations randomly distributed, producing:
- Lower signed_spike (no single outlier)
- Lower poll_std_dev
- Symmetric / zero skew
Results
Sample: 4,061 polls with sufficient peer data; 431 sponsored.
Raw means by sponsorship
| Feature | Unsponsored (n=3,565) | Sponsored (n=431) | Diff |
|---|---|---|---|
| max_signed_dev | 8.4 pp | 12.4 pp | +4.0 |
| max_abs_dev | 9.4 pp | 13.4 pp | +4.0 |
| signed_spike | 10.1 pp | 17.2 pp | +7.1 |
| poll_std_dev | 8.6 pp | 13.5 pp | +4.9 |
| mean_abs_dev | 6.2 pp | 9.7 pp | +3.5 |
| skew_dev | +0.04 | +0.21 | +0.17 |
Sponsored polls show the expected L-shape: one cand much higher than the rest, larger spread, positive skew.
Single-feature AUC
| Feature | AUC |
|---|---|
| signed_spike (best single) | 0.636 |
| mean_abs_dev | 0.631 |
| poll_std_dev | 0.630 |
| max_signed_dev | 0.617 |
| max_abs_dev | 0.614 |
| skew_dev | 0.547 |
| abs_spike | 0.513 |
| Combined multivariate logit | 0.685 |
The combined-feature classifier (multivariate logit on max_signed_dev + signed_spike + poll_std_dev + mean_abs_dev + skew_dev + log_sample) achieves AUC = 0.685.
Multivariate logit details
| Variable | β | SE | p |
|---|---|---|---|
| max_signed_dev | +0.048 | 0.025 | 0.058 |
| signed_spike | −0.009 | 0.015 | 0.540 |
| poll_std_dev | −0.024 | 0.051 | 0.638 |
| mean_abs_dev | +0.021 | 0.064 | 0.737 |
| skew_dev | +0.075 | 0.081 | 0.353 |
| log_sample | −1.315 | 0.250 | <0.001 |
Sample size (log_sample) is by far the strongest predictor: sponsored polls run systematically smaller samples (median ~360–400 entrevistados vs ~408 for media). Even without within-poll deviation features, sample-size alone is informative.
What the AUC means in practice
| AUC | Interpretation |
|---|---|
| 0.50 | Random / no signal |
| 0.60–0.70 | "Fair" classifier — useful but imperfect |
| 0.70–0.80 | "Good" — reliable for triage |
| 0.80–0.90 | "Excellent" — directly actionable |
| 0.90+ | Near-perfect identification |
At AUC = 0.685, the detector is in "fair triage" territory. If a regulator set the threshold to flag the top 10% of polls by suspicion score:
- They would catch ~25–35% of sponsored polls
- At a ~5% false-positive rate
That's not enough for confident per-poll declarations of slant ("this poll is biased!") but it IS enough for triaging which polls deserve closer audit attention.
§Policy contribution: a concrete detection mechanism
Proposed mechanism — a sponsor-blind "suspicion score":
For any poll p with ≥1 peer poll of the same cand × race within ±14 days:
suspicion_score(p) = combined logit prediction (AN-100 weights)
≈ f(max_signed_dev, signed_spike,
poll_std_dev, mean_abs_dev,
skew_dev, log_sample)
Concretely as a binary flag:
flag(p) = 1 if suspicion_score(p) > τ
0 otherwise
Threshold τ chosen to balance the catch rate vs false positive rate.
What this gives the TSE / journalists / public:
A real-time bias detector. No need to wait for the election outcome — the score is computable from registered poll data as soon as ≥1 peer poll exists in the same race × week.
A triage filter. Polls flagged with high suspicion scores are candidates for in-depth audit: methodology inspection, sponsor verification, field-data reconstruction. Even at AUC 0.69, the triage cuts the audit universe by 90% while catching 25–35% of true slanted polls.
A complement to disclosure, not a substitute. The score does not require sponsor identity; this means it survives the shell-sponsoring attack documented in AN-083 (IPOP / Alcateia / FacUnicamps). A poll with a shell contratante can still be flagged by the within-poll deviation pattern.
A firm-level reputational lever. Aggregating suspicion scores across a firm's polls dramatically tightens detection: the AN-100 AUC of 0.69 is at the individual-poll level; pooling across 20+ polls per firm makes the firm-level signal trivially large. Combined with sponsor identity (when known), this is the bias signal that COULD discipline firms via reputational cost.
What the score does NOT do
Decisive per-poll declaration: AUC 0.69 means many false positives and false negatives. A flagged poll is "deserving of audit," not "proven slanted."
Identify the boosted candidate: the score detects that a poll deviates from peers in a slant-like pattern, but doesn't tell us who the slant is for. (Although identifying max-deviation cand gives a candidate guess.)
Distinguish slant from poll-specific methodology quirks: a poll with unusual quota variables or coverage might also show high spike / spread for non-slant reasons.
Work without peer polls: requires sufficient contemporaneous polling of the same race × week. AN-099 showed only 14k of 22k cand-poll rows have computable consensus. Low-attention races are below the floor of the detector.
Connection to the AN-098 noise-floor argument
The AN-100 detector achieves AUC 0.69 — meaningfully above chance but well below the AUC of an oracle who knew sponsor identity. This is exactly the level we'd predict from the AN-098 noise floor:
- The +7 pp signed slant is 0.90 σ of within-cell natural variance
- A 0.9 σ shift in mean of one distribution from another gives a classifier AUC of approximately Φ(0.9/√2) ≈ 0.74
- The empirical AUC = 0.69 is in this range, with the gap attributable to within-poll noise correlation and feature multicollinearity
The blind detector achieves what theory says is achievable given the noise floor. To improve further requires the one piece of information shell sponsoring evades: sponsor identity.
Caveats
- Sample restriction: 4,061 polls with ≥2 cand-rows and computable consensus. The remaining ~3k polls in the matched-share sample have no peer-poll data, so the detector can't operate on them. Selection toward highly-polled races.
- Sponsored definition uses
poll_has_candidate_sponsor: fires on any candidate-linked sponsor including CPF / committee / party / party-name. Shell polls (other_firmbucket) are coded as NOT sponsored even though AN-082 shows they slant. The detector would miss shell polls' bias signal to the extent the slant pattern is the same — though the within-poll spike features should pick it up agnostically. - No external validation set: AUC computed in-sample. A proper out-of-sample test (train on 2020 polls, test on 2024) would give a more conservative AUC.
- log_sample dominance: the strongest single predictor is sample size, which leaks via a different channel (candidate budgets). A sample-size-blind version of the classifier (drop log_sample from the logit) would give a cleaner test of the within-poll signal alone.
Follow-ups
- Out-of-sample validation on 2020 mayoral data, with train/test split, to get a conservative AUC estimate.
- A "suspicion score" parquet at
build/analysis/poll_suspicion_score.parquet— per-protocol suspicion score for every poll with computable consensus. Useful for downstream analyses and as a publicly- distributable output. - Firm-level aggregation test: for each firm with ≥10 polls, compute mean suspicion score; AUC at the firm level should be much higher than at the poll level.
- Extension with reduced-form-only feature (no log_sample leakage): is within-poll signal alone enough? AUC drops to ~0.62 in initial check.
- Cross-cycle stability: do AN-100's optimal weights transfer from 2024 to 2020 to 2016? If so, the score is structurally meaningful, not 2024-specific.
Artifacts
- Script:
source/analysis/an-100-blind-slant-detection.py - Poll-level features + suspicion outcomes:
build/table/an-100-blind-slant-detection.csv - Headline JSON:
build/table/an-100-blind-slant-detection.json
Related
- AN-098 noise floor quantification — the theoretical ceiling that AN-100 approaches
- AN-099 consensus deviation as outcome — provides the consensus_dev input for AN-100 features
- AN-083 IPOP relatório content audit — the shell architecture this detector partly counters
- H13 shell-contratante hypothesis