Methodology: mapping the math_oge bank to official OGE exam task numbers
Question this answers: the open FIPI task bank tags every math_oge question with a КЭС (content codifier, e.g. 7.3 Многоугольники) and an answer_type_code (see scraper/answer_types.py) — but not the official exam task number (№1 through №25). This document derives that mapping from FIPI's own official documents, sourced and read directly (not guessed), and is honest about where the derivation is exact vs. a best-effort group. It mirrors OGE_METHODOLOGY.md's approach for biology and METHODOLOGY.md's for EGE math — same philosophy, separate document since math_oge's own numbering, task shapes, and codifier sections are entirely different from either.
Primary sources (official, read directly — not summaries of summaries)
Fetched from FIPI's own document server (doc.fipi.ru), the authoritative source — not a third-party test-prep site. PDFs are re-hosted here so you can open them directly: Методология on the dashboard links every one of these inline.
| Document | 2026 (current, ratified) | 2027 (draft) |
|---|---|---|
| Спецификация (specification — exam structure, part/type/scoring tables + the "Обобщённый план" appendix with a content code per task number) | ||
| Кодификатор (codifier — full content-element list) | ||
| Демонстрационный вариант (demo — a full real example exam with answer key) |
This document uses the 2026 spec as primary, same reasoning as biology's: it's the current, ratified version, not a draft. Unlike EGE math's METHODOLOGY.md (which targets 2027 because the profile-level EGE is being restructured 19→20 tasks), math_oge's 2027 draft is structurally identical to 2026 — cross-checked directly: both specs' Таблица 2 (part/type/count/score) and Таблица 3 (КЭС-section distribution) are byte-identical (25 tasks, same 2/17/6 split, same 7/1/2/1/1/1/5/1 per-section counts). No restructuring, so no forward-looking reason to prefer the draft. The 2027 documents are still hosted above for reference/comparison.
Exam structure (from the 2026 specification, §6–7, Table 9)
25 tasks, two parts, 31 max points, 235 minutes (3h 55m):
- Part 1, tasks 1–19 (19 tasks, 19 points): short-answer. Of these, only tasks 7 and 13 are single_choice ("Ответы к заданиям 7 и 13 запишите ... в виде одной цифры, которая соответствует номеру правильного ответа" — FIPI's own instruction text to students, unambiguous, quoted directly from the demo variant). Every other Part 1 task (1–6, 8–12, 14–19) is short_answer or matching — "число или последовательность цифр" per the same instruction.
- Part 2, tasks 20–25 (6 tasks, 12 points): answer_type_code = 'extended_answer' — this is Развёрнутый ответ in our schema, and, exactly like EGE math and OGE biology's open-answer parts, has no FIPI answer-checker at all. Deliberately untouched by this project's verification pipeline, same reasoning as every other subject's extended-answer part (see OPEN_ANSWER_METHODOLOGY.md).
Task-by-task content code — from FIPI's own "Обобщённый план" appendix, not inferred
Unlike biology's demo-variant-only derivation, math_oge's 2026 specification has an explicit per-task content-code table (Приложение, "Обобщённый план варианта КИМ ОГЭ 2026 года по МАТЕМАТИКЕ") — the same kind of authoritative table METHODOLOGY.md uses for EGE math. Every row below is that table's own content code (КЭС section 1–8), cross-checked against real scraped codifiers values and real question text (see "What's exact vs. best-effort" below) — not guessed from the demo variant alone, though the demo variant's actual task-by-task content (read directly, page images) confirmed every row.
| Task № | Content code (official) | answer_type_code |
What it actually is |
|---|---|---|---|
| 1–4 | 1–8 (blanket — FIPI's own appendix doesn't narrow this) | short_answer |
Shared real-life scenario module (2026 demo: tire-size lookup table, 4 sub-questions) |
| 5 | 8 | short_answer |
Same shared scenario, a 5th sub-question (reading/computing from the same table) |
| 6 | 1 | short_answer |
Arithmetic expression evaluation (fractions) |
| 7 | 1, 6 | single_choice |
A number's position on a number line |
| 8 | 1, 2 | short_answer |
Arithmetic expression evaluation (radicals) |
| 9 | 3 | short_answer |
Solve an equation |
| 10 | 8 | short_answer |
Probability (equally-likely outcomes) |
| 11 | 5 | matching |
Functions ↔ their graphs |
| 12 | 2 | short_answer |
"Compute via formula" word problem (kinetic energy, force, temperature conversion, etc.) |
| 13 | 3, 6 | single_choice |
Inequality solution read off a number-line picture |
| 14 | 4 | short_answer |
Arithmetic/geometric progressions |
| 15–18 | 7 (identical for all four) | short_answer |
Geometry: triangle trig, circle/tangent angles, trapezoid, area ratios — FIPI's own table gives these the SAME content code and SAME requirement code, not just our data |
| 19 | 7 (bare, no subsection) | single_choice or multi_choice |
"Верны ли следующие утверждения" (true/false geometric statements) — the open bank contains BOTH an older single-select variant and the current multi-select format; both share this task number |
| 20 | 2, 3 | extended_answer |
System of equations |
| 21 | 3 | extended_answer |
Word problem (e.g. mixture/concentration) |
| 22 | 5 | extended_answer |
Function graph + parameter analysis |
| 23–25 | 7 (identical for all three) | extended_answer |
Geometry proofs/computations — again FIPI's own table doesn't distinguish these from each other |
Answer-submission format (separately confirmed against the real FIPI checker)
Same closed-form encodings already reverse-engineered for bio_oge (BIO_OGE_ANALYSIS.md) apply unchanged — confirmed empirically for math_oge too, not assumed: single_choice = bare selected digit; multi_choice = positional binary string over every checkbox in order (not the selected numbers concatenated); matching = concatenated digits in position order; short_answer = plain text/number (decimal point, not comma — see scraper/solve_free_models.py's normalize_answer()). Verified live before the solving rollout: one real question per answer-type/solve_method combo, all four solved and confirmed correct on the first model tried (see WORKLOG.md 2026-09-20).
What's exact vs. best-effort — read this before trusting a number
Same principle as METHODOLOGY.md/OGE_METHODOLOGY.md: where the bank's own metadata (codifier + answer type) uniquely determines a task number, it's exact; where several task numbers share the same signature and can't be told apart, they're grouped honestly rather than guessed.
scraper/assign_math_oge_task_numbers.py encodes these rules. math_oge turned out to classify unusually cleanly:
- Exact via answer type + codifier section, zero ambiguity:
matching→ always task 11 (the only official matching task).multi_choice, or any row (regardless of answer type) tagged with a bare section-7 codifier ("7 Геометрия", no subsection number) → task 19. The bare-vs-subsection distinction matters here specifically: every other geometry task (15–18, 23, 25) is tagged with a specific subsection (7.1–7.5), never bare — confirmed directly against real scraped rows, not assumed.single_choice+ codifier section 3 → task 13;single_choice+ section 1, 2, or 6 → task 7.short_answer+ section 4 → task 14; + section 8 → task 10; + codifier3.1specifically → task 9; + codifier2.1specifically → task 12 (confirmed by reading real sample question text for both:2.1-tagged items are consistently "compute via formula" word problems like task 12's demo example, while2.2–2.5are arithmetic-expression evaluations that belong with tasks 1–8 instead).- Grouped (the bank's metadata genuinely can't separate them further — and for the geometry clusters, this isn't even a limitation of OUR data: FIPI's own appendix table assigns tasks 15–18 the identical content+requirement code, and the same for 23–25):
short_answer+ section 1, or2.2–2.5, or3.3→ grouped"1,2,3,4,5,6,8"(the real-life scenario module plus the two arithmetic-expression tasks — genuinely indistinguishable from each other by codifier alone, and task 1–4's own official content code in the appendix is literally the blanket range "1–8").short_answer+ section 7 → grouped"15,16,17,18".extended_answer+ section 7 → grouped"23,24,25"; + section 2 or 3 → grouped"20,21".
Result on the real 3884-question bank (2026-09-20 run): 1250 exact, 2633 grouped, 1 unclassified (a single question with an empty codifiers array on FIPI's own side — a real scraping gap, not a rule-table gap; left NULL rather than guessed). This is a notably cleaner split than biology's (978 exact / 510 grouped / 491 unclassified out of 1979) — mainly because FIPI's own math spec ships an explicit per-task content code (the Обобщённый план appendix), where biology's spec only gives aggregate section counts.
Implementation
scraper/assign_math_oge_task_numbers.py applies the rules above over codifiers + answer_type_code, writing questions.oge_task_number / oge_task_group / oge_task_confidence — the same three columns bio_oge uses, not a separate pair, since both subjects share exam = 'oge' (see schema.sql's reasoning for why EGE and OGE get separate column pairs but two OGE subjects can share one: each row belongs to exactly one subject, and this script — like assign_oge_task_numbers.py — is scoped to WHERE subject = 'math_oge' only). Because these columns are now genuinely shared between two subjects with unrelated numbering, web/app.py's dropdown/label/filter code was generalized to always resolve a task label and build a filter clause per subject, not by task number alone — see WORKLOG.md 2026-09-20 for the bug this would otherwise have caused (task 11 silently showing biology's label on a math question, or vice versa).
Re-run any time this rule table is revised or the bank is re-scraped — it's idempotent, just re-derives and overwrites these three columns for every math_oge row.