# Mongolia (MN): migration notes

Internal record of the P5 migration from the old scripts to the producer: the discrepancies found, their causes, and who decided what. This is a dated record, so the numbers are as of the migration and not kept current. It is not shipped in the zip.

- **Issue:** pc-28e.7.35 (closed 2026-10-03)
- **Commit:** `09d4006` (worktree commit `9bb1353`), landed in batch 4
- **Parity verdict:** `unexplained` (5 rows only in fresh); accepted by the user
- **Producer:** `custom`, refresh disabled

## Sources

| Input | URL | Notes |
|---|---|---|
| CRC "Unified Postal Code" register (Шуудангийн нэгдсэн код) | https://opendata.gov.mn/dataview/5290 → resolved to `https://opendata.gov.mn/api/v1/media/20251204/d2/d21eff1186a42c72b022fd2117c07ba600a4de07` | The 2025-12-04 upload, the same one the old builder used |

- The portal is a Nuxt SPA. The producer makes the same call the page's own JS makes: `POST https://opendata.gov.mn/api/v1/front/detail/view {"id": 5290}`, takes the one enabled CSV from the file list, and downloads it from `/api/v1/media/<path>`. The resolved URL is recorded in `mn.inputs.json`; `--no-fetch` reuses it.
- The old builder read a hand-downloaded copy, fetched through the page's download button with a headless browser.
- The catalogue only answers POST, so `produce.py` has its own `http_post` (injectable for tests); `common.py` is unchanged.
- opendata.gov.mn has no robots.txt (that URL returns the app page), so this is not the tm situation (orchestrator's note).

## Parity against the live zip (2026-10-03)

Rows: 2,716 live and 2,721 fresh. 0 only in live, 5 only in fresh, 0 changed cells. The first 2,716 rows are byte-identical and in the same order.

| Rows | Change | Cause |
|---|---|---|
| 5 × Uvs aimag (MN-046): 85230 Ховд сум, 85231 "2-р баг, Шивэр", 85233 "1-р баг, Ховд", 85235 "4-р баг, Халиун булаг", 85237 "3-р баг, Ачит" | Only in fresh | The old builder sliced rows `[6:2722]` at fixed line numbers with an exact-count assert, which drops the file's last 5 rows (№ 2717–2721). The upstream upload date is unchanged (2025-12-04), so this is most likely an old-builder bug, not drift. Not proven: the old raw copy is gone |

## Decisions

| Decision | By | Basis |
|---|---|---|
| Accept the 5 added Uvs rows | User, 2026-10-03 (session d33e1b7e, "Accept +5 rows") | Evidence above; recorded in the batch 4 sign-off in `docs/parity/p5.md` |
| Read every row after the header instead of a fixed slice; drop the exact-count assert | Agent | The fixed slice is what cut the rows; the assert would break on any upstream update |
| Fetch through the catalogue API instead of a hand download | Agent | Same file, same portal; scripted fetch per P5 |
| Remove counts from README/descriptor (2,716 rows, 2,707 distinct, 0 collisions, 728 rows); replace the `licence_status.decision` sentence with plain words | Agent | Counts rule |
| Drop `build_postal_codes_product.py` and `publish_r2.py` | Agent | Folded into the producer or replaced by `build.py` |

## Transforms carried over (unchanged)

- The CSV has a preamble, a header row (№, code, place name), then a two-level hierarchy. Each of the 30 area header rows ("{NAME} {ДҮҮРЭГ|АЙМАГ} {code}") becomes a data row with place_name "{Name} {Suffix}" title-cased (e.g. "Багануур Дүүрэг"); localities inherit their area.
- `admin_code1` = ISO 3166-2:MN from a name crosswalk (verified against Wikipedia 2026-09-17), not the file's own area codes. All 9 Ulaanbaatar districts → MN-1 "Ulaanbaatar", district in `admin_name2`.
- "Сүхбаатар" (district vs aimag MN-051) is told apart by the header suffix.
- The old asserts are `ProducerError`s: header row present, gapless № numbering, known suffixes/names, each area once, 5-digit codes, non-empty place, no literal "nan", all 22 admin1 units, unique (postal_code, place_name).

## Open questions

- None recorded beyond the one answered above.
- Any remaining counts in `ATTRIBUTION.txt` / `.licensing-notes.md` belong to the pc-hfz sweep.

## Provenance

Reconstructed 2026-10-05 from the agent transcript `d33e1b7e-5027-498a-a97f-ba914ee2524e/subagents/agent-a66ddb8074462feca.jsonl`, the orchestrator session `d33e1b7e-…` (AskUserQuestion answer), the commit message, the bd notes and close reason, and the batch 4 sign-off in `docs/parity/p5.md`.
