#113: R94 casing is one shared rule — the register's cased twin, else first word only #130

Merged
art merged 4 commits from issue-113-fi-name-casing into main 2026-08-16 05:17:33 +00:00
Owner

Closes #113


R94 casing: one shared rule, first-word unless the register says otherwise

What changed

The R94 casing idiom (CASE WHEN teksti = upper(teksti) THEN initcap(teksti) ELSE teksti END) was written out separately in four places: fi_paikannimi_cl, railways, and twice in stations (rail + metro branches). It now lives once, in a new staging table fi_paikannimi_cased (conf/fimap.yaml), which carries the display-cased name for exactly the series the label layers read (48111/48112/48120/48130 + 14201). All four consumers read name from it — the places side and the stations side cannot drift apart, which is what the #81 principal-station match depends on.

The rule, in order of authority:

  1. Cased rows pass through untouched — the register's own display casing (R94).
  2. An all-caps row whose exact name exists cased anywhere in the register takes that casing. The register is the authority on its own names: AALTO-YLIOPISTO → Aalto-yliopisto (via the 42101 row), S:T MICHEL → S:t Michel (via the 14201 row), NORRA HAGA → Norra Haga (via the 48130 row).
  3. An all-caps hyphen compound (no space/comma) keeps a part capitalised iff that part is itself a register name. MÄNTTÄ-VILPPULA stays Mänttä-Vilppula because Mänttä and Vilppula are both register places; a twin-less AALTO-YLIOPISTO would become Aalto-yliopisto because Yliopisto is not. This is the answer to the issue's "the register does not mark which is which": for hyphen compounds it does, via the parts.
  4. Otherwise only the first letter is capitalised, the rest lower-cased — HELSINGIN YLIOPISTO → Helsingin yliopisto, PASILA, TAVARA → Pasila, tavara (matching the register's own cased ", TAVARA" twins: Hausjärvi, tavara; Imatra, tavara), and a twin-less S:T MICHEL → S:t michel — which is why the colon shape needs no special tokeniser: the fallback never splits a word.

Why this rule

The issue measured the three obvious fixes and found each partial. The combination is what the register itself supports:

  • Twin-borrowing alone fixes 15 of the 47 all-caps 14201 labels and leaves HELSINGIN YLIOPISTO — the more visible of the two metro cases — broken (it has no cased row anywhere).
  • First-word-only alone fixes the visible cases and the whole town,qualifier family but flattens genuine two-part proper names (MÄNTTÄ-VILPPULA, JAKOBSTAD-PEDERSÖRE) — the hyphen-parts rule rescues exactly those.
  • Exception lists are rejected by the project's philosophy (maintained word lists, per-country special-casing).

Measured against the live register: the rule changes 47 all-caps (teksti, kohdeluokka) pairs of the read series, 43 of them to the register-cased or linguistically correct form.

Known remaining limit (documented on the staging table and in docs/country-finland.md): a twin-less all-caps name whose second element the register does not name at all comes out fully lower-cased — KOSKI TL → Koski tl, S:T KARINS → S:t karins, PIEKSÄMÄKI TEMU → Pieksämäki temu, ELÄINPUISTO-ZOO → Eläinpuisto-zoo. Four names; the register carries no cased form and no row for the element, so no signal exists to do better.

Verification (against the live DB)

Reimported the three touched layers (importer/importer -layer places,railways,stations conf/fimap.yaml) plus the new/changed staging tables.

  • Stations: 852 rows unchanged (534 rail + 30 metro + 288 tram — this is a casing fix, not a dedupe; #92's "count unchanged" holds), importance 1:463 / 2:277 / 3:112 unchanged.
  • Railways: 18 294 rows, 3 870 named, all-caps share 0. This is also the first reimport since #86, so the layer table finally carries the normalised names (they were raw register caps before).
  • Places: 267 812 rows at z13/14, 0 all-caps labels, 0 NULL names; zoom-filter counts unchanged (z9 = 1 129, z12 = 72 305).
  • Decoded tiles (scripts/mvt-decode.py): Helsinki z13 tile shows Helsingin yliopisto, Otaniemi z13 shows Aalto-yliopisto, Mänttä area z10 shows Mänttä-Vilppula.
  • Real browser (scripts/cdp-map-check.mjs, no virtual time): the page itself reports rendered station labels Aalto-yliopisto and Helsingin yliopisto at z14, and place labels Mänttä-Vilppula (z10) and Mikkeli (z11).
  • Acceptance tests (fast bucket, make acceptance): ismap 11/11, semap 12/12 (+1 in the 100 s bucket, pre-existing), plmap 13/13, fimap 13/13, world 9/9 — 0 failed anywhere.

Two new acceptance tests were added, in the same commit:

  • stations: "R94: multi-word station names come back in the register's display casing" — the two metro names are present cased and the initcap shapes absent on map_stations_14_fi, and the S:t abbreviation shape is asserted on the register side (the 14201 S:T MICHEL rows normalise to S:t Michel in fi_paikannimi_cased; the map itself shows Mikkeli, the Finnish name).
  • places: "R94: no all-caps Finland place labels, and compound town names keep their parts" — no all-caps label, and Mänttä-Vilppula present while Mänttä-vilppula is absent.

The 2 remaining all-caps station names (TAYS A, TAYS B, Tampere tram stops) are Digiroad's own abbreviations from the tram source (fi_pysakki), not the 14201 register — out of the issue's scope, kept as the source writes them, and noted in docs/country-finland.md.

Docs

docs/country-finland.md's three R94 passages (places, railways, stations) updated to describe the shared rule and its four-name residual limit instead of the old initcap behaviour.

Closes #113 --- # R94 casing: one shared rule, first-word unless the register says otherwise ## What changed The R94 casing idiom (`CASE WHEN teksti = upper(teksti) THEN initcap(teksti) ELSE teksti END`) was written out separately in four places: `fi_paikannimi_cl`, `railways`, and twice in `stations` (rail + metro branches). It now lives once, in a new staging table `fi_paikannimi_cased` (conf/fimap.yaml), which carries the display-cased name for exactly the series the label layers read (48111/48112/48120/48130 + 14201). All four consumers read `name` from it — the places side and the stations side cannot drift apart, which is what the #81 principal-station match depends on. **The rule, in order of authority:** 1. **Cased rows pass through untouched** — the register's own display casing (R94). 2. **An all-caps row whose exact name exists cased anywhere in the register takes that casing.** The register is the authority on its own names: `AALTO-YLIOPISTO` → `Aalto-yliopisto` (via the 42101 row), `S:T MICHEL` → `S:t Michel` (via the 14201 row), `NORRA HAGA` → `Norra Haga` (via the 48130 row). 3. **An all-caps hyphen compound (no space/comma) keeps a part capitalised iff that part is itself a register name.** `MÄNTTÄ-VILPPULA` stays `Mänttä-Vilppula` because Mänttä and Vilppula are both register places; a twin-less `AALTO-YLIOPISTO` would become `Aalto-yliopisto` because Yliopisto is not. This is the answer to the issue's "the register does not mark which is which": for hyphen compounds it does, via the parts. 4. **Otherwise only the first letter is capitalised, the rest lower-cased** — `HELSINGIN YLIOPISTO` → `Helsingin yliopisto`, `PASILA, TAVARA` → `Pasila, tavara` (matching the register's own cased ", TAVARA" twins: Hausjärvi, tavara; Imatra, tavara), and a twin-less `S:T MICHEL` → `S:t michel` — which is why the colon shape needs no special tokeniser: the fallback never splits a word. ## Why this rule The issue measured the three obvious fixes and found each partial. The combination is what the register itself supports: - **Twin-borrowing alone** fixes 15 of the 47 all-caps 14201 labels and leaves `HELSINGIN YLIOPISTO` — the more visible of the two metro cases — broken (it has no cased row anywhere). - **First-word-only alone** fixes the visible cases and the whole town,qualifier family but flattens genuine two-part proper names (`MÄNTTÄ-VILPPULA`, `JAKOBSTAD-PEDERSÖRE`) — the hyphen-parts rule rescues exactly those. - **Exception lists** are rejected by the project's philosophy (maintained word lists, per-country special-casing). Measured against the live register: the rule changes 47 all-caps (teksti, kohdeluokka) pairs of the read series, 43 of them to the register-cased or linguistically correct form. **Known remaining limit** (documented on the staging table and in docs/country-finland.md): a twin-less all-caps name whose second element the register does not name at all comes out fully lower-cased — `KOSKI TL` → `Koski tl`, `S:T KARINS` → `S:t karins`, `PIEKSÄMÄKI TEMU` → `Pieksämäki temu`, `ELÄINPUISTO-ZOO` → `Eläinpuisto-zoo`. Four names; the register carries no cased form and no row for the element, so no signal exists to do better. ## Verification (against the live DB) Reimported the three touched layers (`importer/importer -layer places,railways,stations conf/fimap.yaml`) plus the new/changed staging tables. - **Stations**: 852 rows unchanged (534 rail + 30 metro + 288 tram — this is a casing fix, not a dedupe; #92's "count unchanged" holds), importance 1:463 / 2:277 / 3:112 unchanged. - **Railways**: 18 294 rows, 3 870 named, all-caps share 0. This is also the first reimport since #86, so the layer table finally carries the normalised names (they were raw register caps before). - **Places**: 267 812 rows at z13/14, 0 all-caps labels, 0 NULL names; zoom-filter counts unchanged (z9 = 1 129, z12 = 72 305). - **Decoded tiles** (scripts/mvt-decode.py): Helsinki z13 tile shows `Helsingin yliopisto`, Otaniemi z13 shows `Aalto-yliopisto`, Mänttä area z10 shows `Mänttä-Vilppula`. - **Real browser** (scripts/cdp-map-check.mjs, no virtual time): the page itself reports rendered station labels `Aalto-yliopisto` and `Helsingin yliopisto` at z14, and place labels `Mänttä-Vilppula` (z10) and `Mikkeli` (z11). - **Acceptance tests** (fast bucket, `make acceptance`): ismap 11/11, semap 12/12 (+1 in the 100 s bucket, pre-existing), plmap 13/13, **fimap 13/13**, world 9/9 — 0 failed anywhere. Two new acceptance tests were added, in the same commit: - `stations`: "R94: multi-word station names come back in the register's display casing" — the two metro names are present cased and the initcap shapes absent on `map_stations_14_fi`, and the S:t abbreviation shape is asserted on the register side (the 14201 `S:T MICHEL` rows normalise to `S:t Michel` in `fi_paikannimi_cased`; the map itself shows Mikkeli, the Finnish name). - `places`: "R94: no all-caps Finland place labels, and compound town names keep their parts" — no all-caps label, and `Mänttä-Vilppula` present while `Mänttä-vilppula` is absent. **The 2 remaining all-caps station names** (`TAYS A`, `TAYS B`, Tampere tram stops) are Digiroad's own abbreviations from the tram source (fi_pysakki), not the 14201 register — out of the issue's scope, kept as the source writes them, and noted in docs/country-finland.md. ## Docs docs/country-finland.md's three R94 passages (places, railways, stations) updated to describe the shared rule and its four-name residual limit instead of the old initcap behaviour.
The casing idiom (CASE WHEN teksti = upper(teksti) THEN initcap(teksti) ...) was
written out separately in four places: fi_paikannimi_cl, railways, and twice in
stations. initcap starts a new word at every non-alphanumeric character, so the
map rendered "Aalto-Yliopisto" and "Helsingin Yliopisto" (Finnish wants
Aalto-yliopisto, Helsingin yliopisto) and broke the S:t abbreviation
(S:T MICHEL → "S:T Michel").

The rule now lives once, in a new staging table fi_paikannimi_cased carrying the
display-cased name for the series the label layers read (48111/48112/48120/48130
+ 14201); places, railways and stations all read `name` from it, so the #81
principal-station match (station name vs town name normalised identically) cannot
drift. The rule, in order of authority: cased rows pass through; an all-caps row
whose exact name exists cased anywhere in the register takes that casing (the
register is the authority on its own names — AALTO-YLIOPISTO → Aalto-yliopisto via
the 42101 row, S:T MICHEL → S:t Michel via the 14201 row); an all-caps hyphen
compound keeps a part capitalised iff that part is itself a register name
(MÄNTTÄ-VILPPULA stays Mänttä-Vilppula because Mänttä and Vilppula are both
register places); otherwise only the first letter is capitalised (HELSINGIN
YLIOPISTO → Helsingin yliopisto, PASILA, TAVARA → Pasila, tavara). The colon
shape needs no special tokeniser: the fallback never splits a word.

Measured against the live register: the rule changes 47 all-caps (teksti,
kohdeluokka) pairs of the read series, 43 to the register-cased or linguistically
correct form. Four names remain fully lower-cased where the register carries no
cased form and no row for the second element (Koski tl, S:t karins, Pieksämäki
temu, Eläinpuisto-zoo) — documented as the rule's known remaining limit.

Reimported the three touched layers and verified against the live DB: station
count 852 unchanged (534 rail + 30 metro + 288 tram; imp 1:463/2:277/3:112
unchanged), railways 18 294 rows / 3 870 named / 0 all-caps, places 267 812 rows
/ 0 all-caps / 0 NULL names; decoded tiles show "Helsingin yliopisto" (Helsinki
z13) and "Aalto-yliopisto" (Otaniemi z13); a real-browser CDP render has the page
report both labels at z14, plus "Mänttä-Vilppula" at z10 and "Mikkeli" at z11.

Two acceptance tests added in the same commit (R94 stations: the metro names are
cased and the initcap shapes gone, with the S:t shape asserted on the register
side; R94 places: no all-caps label and Mänttä-Vilppula keeps its parts). make
acceptance: ismap 11/11, semap 12/12, plmap 13/13, fimap 13/13, world 9/9, 0
failed. The 2 remaining all-caps station names (TAYS A/B, Tampere tram stops)
are Digiroad abbreviations from the tram source, not the 14201 register.
art changed title from Closes #113 to #113: R94 casing is one shared rule — the register's cased twin, else first word only 2026-08-15 17:10:13 +00:00
The stations test built `passed` entirely from subqueries, so nothing in it
referred to the rows it selected: run it with an outer WHERE that matches nothing
and it still returns true. Both metro stations losing their names looks exactly
like both initcap shapes being absent, which is the thing it was written to catch.
It now counts the two names it claims must be present — verified red when they are
missing, green as the database stands.

The places test's `length(name) > 3` was a threshold standing in for an idea:
`name <> lower(name)` says the actual exemption, which is a name with no cased
letters to shout with. Finland has neither kind today.

`twins` picked among several cased forms of one name by an ORDER BY that did not
end in a unique column, so the winner was plan-dependent. 21 keys in the register
have two forms (Ala-aho/Ala-Aho, Övre kärret/Övre Kärret); none is currently
reached by an all-caps row of the read series, so this is reproducibility, not a
visible bug.

Also recorded R94's acceptance tests in docs/country-requirements.md, and wrote
down what reviewing this turned up: water_bodies is a fifth consumer of the same
register that never got the rule, so SAIMAA, PÄIJÄNNE and PIELINEN ship in
capitals. Backed out of this branch rather than shipped unverified — the fix is
two kohdeluokka in the WHERE and the rule was measured against them (all 25
all-caps water names come back correct), but it needs a water_bodies re-import to
prove, and fetching the 5.65 GB MTK source hits my disk quota. Filed separately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
art-bot force-pushed issue-113-fi-name-casing from aa4a6e3c04 to 1b6b1bcde3 2026-08-15 18:50:26 +00:00 Compare
# Conflicts:
#	conf/fimap.yaml
PR #130 (issue #113) made R94 casing one shared rule in the fi_paikannimi_cased
staging pass, read by places, stations and railways. The review (#133) found a
fifth consumer of the same register: water_bodies joined fi_paikannimi raw
(kohdeluokka 36201/36301) and took teksti unnormalised, so the ten biggest lakes
shipped in capitals — SAIMAA, PÄIJÄNNE, PIELINEN, NÄSIJÄRVI, KALLAVESI, KEITELE,
PUULA, KIANTAJÄRVI, SUONTEE, ALA-KEITELE.

The fix is exactly the two-line change #133 specified, plus its acceptance test:

- fi_paikannimi_cased: 36201 and 36301 join the kohdeluokka WHERE list. The rule
  needed no extension — measured over the register, 0 of 155 844 water-name rows
  come out all-caps, and all 25 all-caps names in the two series come back
  correct (Bottniska viken, Skärgårdshavet via cased twins; Saimaa, Päijänne via
  the first-word rule; Ala-Keitele keeps its capital because Keitele is itself a
  register name).
- water_bodies: staging dep fi_paikannimi -> fi_paikannimi_cased, and the lateral
  join reads n.name instead of n.teksti.
- New acceptance test on the water_bodies layer: "R94: no all-caps Finland water
  body labels, and Saimaa is named", paired with the presence check the stations
  review demanded — absence of all-caps is also what a broken name join looks
  like.

Verified against the live DB after the re-import (fetch was a no-op, the MTK
sources were already on disk): map_water_bodies_14_fi has 0 all-caps names (was
10), Saimaa and the ten lakes are cased, and the row count is unchanged at
171 646. places (267 812), railways (18 294) and stations (852) are unchanged —
the staging table gained water rows but none of the three layers' outputs
moved. All 15 fimap acceptance tests pass (-acceptance all), `make acceptance`
is green across every config, and decoded tiles carry the cased names (Saimaa,
Päijänne, Näsijärvi) on the wire.

Also reworded the stale notes: the "NOT YET every consumer … Issue #133" block on
the staging pass now says water_bodies IS covered, and R94's entry in
docs/country-requirements.md records the closed gap.

Merged origin/main first (7 commits: #93, #115, #120, #121 and their reviews).
Author
Owner

#133 is now landed on this branch — water_bodies is the fifth consumer of the register, and it reads name from the shared pass like everywhere else.

What landed (commit 676b753, on top of the origin/main merge):

  • fi_paikannimi_cased: 36201, 36301 joined the kohdeluokka WHERE list — the whole fix, exactly as #133 specified. The rule needed no extension; measured against the rebuilt register, 0 of 155 844 water-name rows come out all-caps, and all 25 all-caps names in the two series come back correct (Bottniska viken / Skärgårdshavet / Hangon itäinen selkä via cased twins, the lakes via the first-word rule, Ala-Keitele keeping its capital because Keitele is itself a register name).
  • water_bodies: staging dep fi_paikannimi → fi_paikannimi_cased, and the lateral join reads p.name instead of p.teksti.
  • Acceptance test on the water_bodies layer: "R94: no all-caps Finland water body labels, and Saimaa is named", with the presence half #133 insisted on (absence of all-caps is also what a broken name join looks like).

Verified against the live DB after importer/importer -layer water_bodies conf/fimap.yaml (fetch was a no-op — the MTK sources were on disk, so no 5.65 GB re-download):

  • map_water_bodies_14 fi: 0 all-caps (was 10), Saimaa present, all ten lakes cased (Saimaa, Päijänne, Pielinen, Näsijärvi, Kallavesi, Keitele, Puula, Kiantajärvi, Suontee, Ala-Keitele), row count unchanged at 171 646.
  • No side effects: places (267 812), railways (18 294), stations (852) row counts identical — the staging table gained water rows, none of the three layers' outputs moved — and their R94 tests still pass.
  • Acceptance: fimap -acceptance all 15/15, make acceptance green across all five configs.
  • Decoded tiles: z14 tiles for Saimaa, Päijänne and Näsijärvi carry the cased names on the wire.

Also reworded the now-stale notes: the "NOT YET every consumer … Issue #133" block on the staging pass (added in 1b6b1bc) now records water_bodies as covered, and R94's entry in docs/country-requirements.md describes the closed gap rather than the open one.

#133 is now landed on this branch — water_bodies is the fifth consumer of the register, and it reads `name` from the shared pass like everywhere else. What landed (commit 676b753, on top of the origin/main merge): - **fi_paikannimi_cased**: `36201, 36301` joined the kohdeluokka WHERE list — the whole fix, exactly as #133 specified. The rule needed no extension; measured against the rebuilt register, 0 of 155 844 water-name rows come out all-caps, and all 25 all-caps names in the two series come back correct (Bottniska viken / Skärgårdshavet / Hangon itäinen selkä via cased twins, the lakes via the first-word rule, Ala-Keitele keeping its capital because Keitele is itself a register name). - **water_bodies**: staging dep `fi_paikannimi` → `fi_paikannimi_cased`, and the lateral join reads `p.name` instead of `p.teksti`. - **Acceptance test** on the water_bodies layer: "R94: no all-caps Finland water body labels, and Saimaa is named", with the presence half #133 insisted on (absence of all-caps is also what a broken name join looks like). Verified against the live DB after `importer/importer -layer water_bodies conf/fimap.yaml` (fetch was a no-op — the MTK sources were on disk, so no 5.65 GB re-download): - map_water_bodies_14 fi: **0 all-caps** (was 10), Saimaa present, all ten lakes cased (Saimaa, Päijänne, Pielinen, Näsijärvi, Kallavesi, Keitele, Puula, Kiantajärvi, Suontee, Ala-Keitele), row count unchanged at **171 646**. - No side effects: places (267 812), railways (18 294), stations (852) row counts identical — the staging table gained water rows, none of the three layers' outputs moved — and their R94 tests still pass. - Acceptance: fimap `-acceptance all` **15/15**, `make acceptance` green across all five configs. - Decoded tiles: z14 tiles for Saimaa, Päijänne and Näsijärvi carry the cased names on the wire. Also reworded the now-stale notes: the "NOT YET every consumer … Issue #133" block on the staging pass (added in 1b6b1bc) now records water_bodies as covered, and R94's entry in docs/country-requirements.md describes the closed gap rather than the open one.
Collaborator

Review

Verified: go test ./... green, and the fimap suite runs 15/15 against the live database — fi_paikannimi_cased exists and is populated there, so this went through a real import rather than a config-only edit.

I checked the casing rule's claims against the register rather than taking the PR body's word, and all of them hold:

Aalto-yliopisto   Helsingin yliopisto   Mänttä-Vilppula
Norra Haga        Pasila, tavara        S:t Michel

Zero all-caps names survive into fi_paikannimi_cased. Two specific things I went looking for and did not find:

  • The hyphen rule's premise is sound. regnames draws from the whole register, so a standalone YLIOPISTO row anywhere would capitalise the wrong half of AALTO-YLIOPISTO. There are 0 such rows, so rule 3 does what the PR body says it does.
  • The cased-twin ambiguity is inert. 21 lowercase keys carry more than one distinct cased form (Ala-aho/Ala-Aho, Övre kärret/Övre Kärret, …), which would make DISTINCT ON a coin flip. None of them has a single all-caps consumer in the five label classes, so the ORDER BY tiebreak never actually decides anything today. Worth knowing it's luck rather than design, and that the ordering is what will decide it the first time a collision does land.

The lower-cased results read correctly for both languages — Bottniska viken, Hangon itäinen selkä, Hausjärvi, tavara — and the four names the PR admits it cannot do better on (Koski tl, S:t karins, Pieksämäki temu, Eläinpuisto-zoo) are genuinely unreachable from the register.

The places R94 test can't say which half broke

The stations twin was strengthened in 1b6b1bc to return names=... so a failure names what it found. The places test next to it still returns only passed:

SELECT NOT EXISTS (... name = upper(name) ...)
   AND NOT EXISTS (... name = 'Mänttä-vilppula')
   AND EXISTS     (... name = 'Mänttä-Vilppula') AS passed

That is three independent failure modes — labels started shouting, the compound got flattened, or the name join broke entirely — collapsing into one bit. The run confirms it: PASS (118ms, 1 row(s)) with no detail at all. A count of all-caps labels plus the matched compound forms would cost nothing and make a red run self-explaining, the same way the stations version now does.

Merge note: R105 must grow to 16

This branch adds a 16th Finland staging table. #124 (PR #137) ships an R105 test that names 15 and whose comment states the invariant "The list must be the staging: block's list, and nothing else." After both merge, fi_paikannimi_cased is the one staging table R105 does not cover — which is the wrong table to miss, since a silently-empty shared casing pass is exactly the failure that would drift all five consumers at once.

The two branches also conflict directly in conf/fimap.yaml (both add acceptance_tests: to water_bodies and stations). The conflict is additive on both sides, so resolving it is mechanical — just don't let the R105 list slip through while doing it.

## Review Verified: `go test ./...` green, and the fimap suite runs 15/15 against the live database — `fi_paikannimi_cased` exists and is populated there, so this went through a real import rather than a config-only edit. I checked the casing rule's claims against the register rather than taking the PR body's word, and all of them hold: ``` Aalto-yliopisto Helsingin yliopisto Mänttä-Vilppula Norra Haga Pasila, tavara S:t Michel ``` Zero all-caps names survive into `fi_paikannimi_cased`. Two specific things I went looking for and did not find: - **The hyphen rule's premise is sound.** `regnames` draws from the whole register, so a standalone `YLIOPISTO` row anywhere would capitalise the wrong half of `AALTO-YLIOPISTO`. There are 0 such rows, so rule 3 does what the PR body says it does. - **The cased-twin ambiguity is inert.** 21 lowercase keys carry more than one distinct cased form (`Ala-aho`/`Ala-Aho`, `Övre kärret`/`Övre Kärret`, …), which would make `DISTINCT ON` a coin flip. None of them has a single all-caps consumer in the five label classes, so the `ORDER BY` tiebreak never actually decides anything today. Worth knowing it's luck rather than design, and that the ordering is what will decide it the first time a collision does land. The lower-cased results read correctly for both languages — `Bottniska viken`, `Hangon itäinen selkä`, `Hausjärvi, tavara` — and the four names the PR admits it cannot do better on (`Koski tl`, `S:t karins`, `Pieksämäki temu`, `Eläinpuisto-zoo`) are genuinely unreachable from the register. ### The places R94 test can't say which half broke The stations twin was strengthened in 1b6b1bc to return `names=...` so a failure names what it found. The places test next to it still returns only `passed`: ```yaml SELECT NOT EXISTS (... name = upper(name) ...) AND NOT EXISTS (... name = 'Mänttä-vilppula') AND EXISTS (... name = 'Mänttä-Vilppula') AS passed ``` That is three independent failure modes — labels started shouting, the compound got flattened, or the name join broke entirely — collapsing into one bit. The run confirms it: `PASS (118ms, 1 row(s))` with no detail at all. A count of all-caps labels plus the matched compound forms would cost nothing and make a red run self-explaining, the same way the stations version now does. ### Merge note: R105 must grow to 16 This branch adds a 16th Finland staging table. #124 (PR #137) ships an R105 test that names 15 and whose comment states the invariant "The list must be the `staging:` block's list, and nothing else." After both merge, `fi_paikannimi_cased` is the one staging table R105 does not cover — which is the wrong table to miss, since a silently-empty shared casing pass is exactly the failure that would drift all five consumers at once. The two branches also conflict directly in `conf/fimap.yaml` (both add `acceptance_tests:` to `water_bodies` and `stations`). The conflict is additive on both sides, so resolving it is mechanical — just don't let the R105 list slip through while doing it.
Collaborator

Rebased into a dependency chain — this PR is now the base of one, so it merges first and is unchanged (676b753, no force-push).

main ← #130 (#113) ← #134 (#122) ← #136 (#123) ← #137 (#124)

The conf/fimap.yaml conflict with #137 is gone: #137 now sits downstream and carries both sides of the R94 blocks. #137's R105 test was extended to 16 staging tables there, so fi_paikannimi_cased is covered the moment both are in.

Verified on the chain tip: go test ./... green, and all five acceptance suites pass against the live DB (ismap 26/26, semap 26+1 not-run, plmap 24/24, fimap 29/29, world 9/9).

**Rebased into a dependency chain** — this PR is now the base of one, so it merges first and is unchanged (`676b753`, no force-push). ``` main ← #130 (#113) ← #134 (#122) ← #136 (#123) ← #137 (#124) ``` The `conf/fimap.yaml` conflict with #137 is gone: #137 now sits downstream and carries both sides of the R94 blocks. #137's R105 test was extended to 16 staging tables there, so `fi_paikannimi_cased` is covered the moment both are in. Verified on the chain tip: `go test ./...` green, and all five acceptance suites pass against the live DB (ismap 26/26, semap 26+1 not-run, plmap 24/24, fimap 29/29, world 9/9).
art merged commit 676b753ae2 into main 2026-08-16 05:17:33 +00:00
art deleted branch issue-113-fi-name-casing 2026-08-16 05:17:33 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
art/ismap!130
No description provided.