Skip to content

Fix/single country regions - #1035

Merged
AlexAxthelm merged 8 commits into
epic/v2from
fix/single-country-regions
Oct 28, 2026
Merged

AlexAxthelm merged 8 commits into
epic/v2from
fix/single-country-regions

Conversation

@AlexAxthelm

Copy link
Copy Markdown
Collaborator

Solve the Japan/JP discussion by not allowing single (or empty) geographic regions: A region must have at least two members.

Import parses and converts single-country regions "JP": ["JP"] -> JP, but not "Japan": "JP"

Updates the existing data files, schema, etc to follow on to that.


Why

One country code is always a country (Jacob's decision on #998). Until now a
pathway could declare "Japan": ["JP"] as a region, and that was a different
scope from the country JP: a value recorded at one was never found from the
other, and the two rendered differently. Regions with no members were allowed
too, and the app had to treat them as "unknown membership" everywhere.

This makes both shapes impossible: in the data, in the schema, and in the
importer that writes the data.

What changes

Data (fix(data): make one-member regions countries)
16 pathways had one-member regions. Each member moves into
geography.country, the region is removed, and every reference to the old
label becomes the code: keyFeatures and dataAvailability geography, the rows
of the 14 NGFS timeseries files, and index.gen.ts. Only geography values
change; row and entry counts are the same.

  • IEA STEPS/CPS 2025: United States, Brazil, Russia, India, Japan
  • NGFS 2024 GCAM (7): Argentina, Brazil, Canada, Colombia, India, Indonesia,
    Japan, Mexico, Pakistan, Russia, South Africa, South Korea, Taiwan
  • NGFS 2024 REMIND (7): India, Japan, United States of America

The diff is about 120k lines, almost all timeseries rows where only
geography changed. Review the metadata files and skim the timeseries.

Schema
geography.v1.json requires two or more members per region (minItems: 2),
so one-member and empty regions both fail schema:check. The generated type
is now [CountryCode, CountryCode, ...CountryCode[]]. The four
testdata/valid fixtures that had empty regions now have real members.

Pathway importer (scripts/import-pathway-data.ts)

  • JP or JP: [JP] in the Regions cell is the country JP (deduped).
  • Japan: [JP] is an import error: the workbook has to write JP.
  • A bare label, Label: [], or a region whose only codes aren't ISO is an
    import error.
  • Errors block every write, so a workbook with these won't import until it's
    fixed.

Benchmark data
validateTimeseries still rejects a row labelled "Japan" when the pathway
declares JP. The error now ends with -- use "JP" when the label is a
declared country's English name. It's an exact match on Intl's short name
("United States", not "United States of America"), so treat it as a hint.

App cleanup
Code and tests for unmapped and one-member regions are gone: the "1 country"
tooltip wording, 8 tests for regions the schema now rejects, and comments
describing empty regions as real data. The guards against malformed data
stay.

Things to know

  • Display names: these countries now show their ISO names, so NGFS's
    "United States of America" and IEA's "United States" read the same.
  • Prep repo contract: benchmark files must label these geographies with
    their codes. RMI/tpr_benchmark_data_preparation needs a change (issue to
    follow).
  • Classification workbook: the importer now refuses Japan: [JP], so the
    current workbook has to be fixed before the next import. Key-feature and
    data-availability cells must say JP too: a cell that still says "Japan"
    after the Regions cell is fixed is an unresolved geography, so the import
    notes it and drops that value. (tpr-tpc issue to follow.)
  • Shared comparison links: a link with geography=<id>:Japan falls back
    to that column's default geography. Nothing else stores a scope selection.
  • Relation to Feat/resolver (key features) #998: the resolver needs no change. It has no special case
    for one-member regions, and these labels are now plain country codes, which
    it already handles. The two PRs don't touch the same files.

Testing

  • npx vitest run: 85 files, 1297 tests
  • npm run lint, npm run format:check, npm run build
  • npm run schema:check: 129 valid, 0 invalid
  • tsc: same 201 app / 124 scripts errors as epic/v2 (not a CI gate)
  • The data conversion was checked file by file: same row and entry counts,
    only geography values differ, and no metadata file still has a one-member
    region.

🤖 Generated with Claude Code

repro and others added 7 commits October 26, 2026 15:25
A region whose only member is one country code is that country (#998):
IEA's "Japan": ["JP"] is JP. Kept as a region, it was a different scope
from JP, so a value recorded at one was never found from the other, and
the two displayed differently. Only `LA: [LA]`, a region named by its
own code, was read as a country before.

* buildGeography reads any one-member region as its country, whatever
  the label. When the label is not the code, it says so in the report
  ('region "Japan" has one member, imported as country JP').
* The label -> code renames reach resolveGeography, so key-feature and
  data-availability cells that still say "Japan" resolve to JP.
* A country listed standalone and as a one-member region, or reached by
  two labels, is listed once, with a note.
* Key-feature rows for "Japan" and "JP" at the same scope collapse as
  before when their values agree. When they disagree, the import fails
  and names both, rather than keeping the first one without a word.

The data in src/data still has these regions; a codemod follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A region with no members matches nothing; the classifier tends to pick
these up from a passing mention of a place the pathway doesn't cover.
The import used to keep one as an empty region with a note. It now
stops, naming the pathway and the label, so the Regions cell is fixed
in the workbook (#998).

This covers a bare label (`Atlantic Basin`), an empty list
(`Atlantic Basin: []`), and a list whose codes are all non-ISO
(`Kosovo: [XK]`, where the message names the dropped codes).

No metadata in src/data has one today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One country code is always a country (#998). Sixteen pathways declared
regions with a single member, so "Japan" and JP were different scopes
and a value recorded at one was never found from the other:

* IEA STEPS/CPS 2025: United States, Brazil, Russia, India, Japan.
* NGFS 2024 GCAM (7 files): Argentina, Brazil, Canada, Colombia, India,
  Indonesia, Japan, Mexico, Pakistan, Russia, South Africa, South Korea,
  Taiwan.
* NGFS 2024 REMIND (7 files): India, Japan, United States of America.

Each member is now listed in geography.country and its region is gone.
Every reference to the old label is renamed to the code: keyFeatures
and dataAvailability geography, the timeseries rows of the 14 NGFS
files, and the generated timeseries index. Only geography values
change; row and entry counts are the same.

These countries now display under their ISO names, so NGFS's "United
States of America" and IEA's "United States" read the same.

Prepared benchmark data must now label these geographies with their
codes; validateTimeseries rejects the old labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A one-member region like `Japan: [JP]` is that country, and step 1 imported it
as JP, with a lookup so other cells that still said "Japan" also found JP. That
let the workbook keep writing places two ways. The import now fails on it and
asks for the code, so the workbook, the data and the benchmark files all use
one spelling.

* `Label: [XX]` with one ISO member is an import error when the label is not
  the code, including when other codes were dropped as non-ISO
* `XX: [XX]` and a bare `XX` still import as the country, listed once
* remove the rename lookup and the "Japan"/"JP" disagreement check, which
  only existed to support the conversion; resolveGeography, buildKeyFeatures,
  parseCoverage and buildDataAvailability are back to their epic/v2 signatures
* tests: the error, members counted after dropping non-ISO codes, and the
  `JP; JP: [JP]` dedupe

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One country code is always a country (#998), and a region with no members
matches nothing, so neither is a region. The importer already refuses both;
the schema now does too, so a hand-edited or upstream file can't bring them
back.

* geography.v1.json: region member arrays get `minItems: 2`; both
  descriptions say a single country belongs in `country`
* keep the generated type `CountryCode[]` with an array-level tsType: the
  tuple json-schema-to-typescript would emit adds 19 tsc errors in app
  tests that still build unmapped or one-member regions on purpose
* testdata/valid: the four fixtures with an empty region get real members
* validateData tests: `Japan: [JP]` fails in v1 and v2, an empty region
  fails, and the two boundary tests use a two-member region

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every region now has two or more members (#998), so the app's code and tests
for regions with none or one described data the schema rejects. This removes
that, and lets the generated type say what the schema says.

* geography.v1.json: drop the array-level tsType, so region members generate
  as a `[CountryCode, CountryCode, ...CountryCode[]]` tuple like the repo's
  other minItems arrays; no new tsc errors
* RegionMembersTooltip: drop the "1 country" wording; the no-mapping copy stays
  for an undeclared label or missing geography
* comparisonScope, geographyUtils, keyFeatureScope: comments no longer
  describe empty regions as real data; the empty-set guards stay for
  malformed input
* tests: remove the unmapped-region cases (tooltip, comparison and detail
  pages, keyFeatureScope, scopeSeed, regionMemberCodes) and give the
  remaining fixtures two or more members; the comparisonScope guard test is
  kept as a malformed-data case

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One country code is always a country (#998), so a benchmark file that still
labels a row "Japan" is rejected once the metadata says JP. The error now says
what to write instead.

* validateTimeseries: when a rejected geography is the English name of a
  country the pathway declares (standalone or as a region member), append
  `-- use "JP"`; the match ignores case and surrounding space
* names come from Intl.DisplayNames, since geographyUtils' locale JSON cannot
  load under the ts-node scripts; a name spelled another way, like "United
  States of America", keeps the plain error
* tests: standalone country, region member across two pathways, undeclared
  country, and a name Intl spells differently

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AlexAxthelm AlexAxthelm self-assigned this Oct 27, 2026
@github-actions

Copy link
Copy Markdown

Expected version change and release notes

🚨 WARNING: This PR is not expected to trigger a new version

To trigger a version bump, use at least one conventional commit message in this branch. See: https://www.conventionalcommits.org/en/v1.0.0/

@github-actions

Copy link
Copy Markdown

Azure Static Web Apps: Your stage site is ready! Visit it here: https://proud-glacier-0f640931e-1035.westus2.2.azurestaticapps.net

@AlexAxthelm
AlexAxthelm marked this pull request as ready for review October 27, 2026 17:05
@jacobvjk
jacobvjk self-requested a review October 27, 2026 17:22

@jacobvjk jacobvjk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

nb: a little quirk is this

Image

shown as "USA" instead of "United States of America" because it is defined as a region of three ISO codes. Not wrong

@AlexAxthelm
AlexAxthelm merged commit 4415964 into epic/v2 Oct 28, 2026
11 checks passed
@AlexAxthelm
AlexAxthelm deleted the fix/single-country-regions branch October 28, 2026 13:53
@AlexAxthelm

Copy link
Copy Markdown
Collaborator Author

lgtm

nb: a little quirk is this

Image shown as "USA" instead of "United States of America" because it is defined as a region of three ISO codes. Not wrong

Oh. yeah. That is weird. Not wrong. Something to note in the prep repos?

@jacobvjk

Copy link
Copy Markdown
Collaborator

lgtm
nb: a little quirk is this
Image
shown as "USA" instead of "United States of America" because it is defined as a region of three ISO codes. Not wrong

Oh. yeah. That is weird. Not wrong. Something to note in the prep repos?

I'm adding tasks. It's an open question though, we might well decide it's fine as it is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants