# add-country: the repeatable shape for onboarding a country #178

Merged
art merged 4 commits from add-country-skill into main 2026-08-20 10:08:39 +00:00
Owner

add-country: the repeatable shape for onboarding a country


Distilled from the Greece work (#171 catalogue, #172 pilot, #173 glyphs) — the
first country added since the four that established the mechanism. The skill is
the country-level plan those per-layer issues are the pieces of.

What it captures

  • The gate (R5/R6/R2): parallel-subagent source research per category, live
    verification of load-bearing endpoints, per-layer GO/ABSENT verdicts, and the
    docs/country-.md catalogue in the reference shape (R129–R136).
  • Glyph de-risking (R7): the test that keeps it green, data-independent.
  • The pilot: conf// per R17, mask+cover, import- chaining import-world
    (R107), acceptance tests (R119/R34/R22/R120) in the same commit.
  • Credentialed fetchers (R142): one provider per agency, stdlib crypto, no key
    in config or URL.
  • The layers in catalogue order, each with its acceptance tests.
  • The pitfalls Greece learned the hard way: NUTS vs ISO codes (EL not GR),
    transient API statuses (a default-to-error poll fails on "Queued"), column
    casing as a schema property (Code_18 → code_18), portal-vs-dataset licence
    gaps (R2), and never two country-work subagents in one checkout.

Cross-linked from the top of docs/country-requirements.md and registered in
AGENTS.md's skills index.

# add-country: the repeatable shape for onboarding a country --- Distilled from the Greece work (#171 catalogue, #172 pilot, #173 glyphs) — the first country added since the four that established the mechanism. The skill is the country-level plan those per-layer issues are the pieces of. ## What it captures - **The gate** (R5/R6/R2): parallel-subagent source research per category, live verification of load-bearing endpoints, per-layer GO/ABSENT verdicts, and the docs/country-<cc>.md catalogue in the reference shape (R129–R136). - **Glyph de-risking** (R7): the test that keeps it green, data-independent. - **The pilot**: conf/<cc>/ per R17, mask+cover, import-<cc> chaining import-world (R107), acceptance tests (R119/R34/R22/R120) in the same commit. - **Credentialed fetchers** (R142): one provider per agency, stdlib crypto, no key in config or URL. - **The layers** in catalogue order, each with its acceptance tests. - **The pitfalls Greece learned the hard way**: NUTS vs ISO codes (EL not GR), transient API statuses (a default-to-error poll fails on "Queued"), column casing as a schema property (Code_18 → code_18), portal-vs-dataset licence gaps (R2), and never two country-work subagents in one checkout. Cross-linked from the top of docs/country-requirements.md and registered in AGENTS.md's skills index.
Distilled from the Greece work (#171–#173), the first country added since the
four that established the mechanism: the research gate and go/no-go catalogue
(R5/R6/R2) in docs/country-<cc>.md, glyph de-risking (R7), the mask+cover pilot,
credentialed-fetcher providers (R142), then the layers — each with acceptance
tests. Captures the pitfalls the Greece run learned the hard way: NUTS vs ISO
country codes (Greece EL not GR), transient API statuses (a poll that
defaults-to-error fails on "Queued"), column casing as a schema property
(Code_18 → code_18 after the loader), portal-vs-dataset licence gaps (R2), and
never running two country-work subagents in one checkout.

Registered in AGENTS.md's skills index and cross-linked from the top of
docs/country-requirements.md (the "read before adding a country" document).
The skill was distilled from the Greece catalogue while that catalogue's central
verdict was wrong, and it copied both the wrong numbers and the wrong lesson. It
said Greece landed "five shared layers (mask, borders, cover, water)" — naming
four — and "seven absences" while naming six. It is seven shared layers and four
absences, because roads and railways are not absences: the TEN-T road network
(Egnatia Odos, CC BY 4.0, 4 853 km, national) and GAIAOSE's Railnet service
(8 865 segments / 4 635 km, 443 named stations) were both published and both
missed.

So the skill now carries the two research rules that would have caught it.

A timeout is not a verdict. geodata.gov.gr is an AAAA-only host and the research
box has no IPv6 route, so it could only ever time out there; "the geoportal is
offline" was a fact about the network path written down as a fact about the
source. The same inference was applied to ktimanet.gr, which answers 200. The
step now names the checks — getent ahostsv4/ahostsv6, ip -6 route show default, a
second vantage, the Archive CDX API for dating a real outage.

And search the EU aggregator, not only the national portal and the ministries.
Every transport source was indexed on data.europa.eu the whole time; a
ministry-first search missed them because the publishers are state-owned
operators under their own names. The step now says to search operator names —
motorway concessionaires, the rail infrastructure manager, port and airport
authorities.

Two smaller ones: an ABSENT verdict is never tested by anything downstream, the
way a GO verdict is tested by the import that follows it, so absences get the
higher standard of evidence; and a PR body written against the first commit and
never updated costs more trust than it saves time.

Also folded in what the #177 review turned up: wire a provider into both fetch
and list and update the docs for both, and re-mint the session token rather than
holding one for the whole fetch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8HxnPejASTBhLaSxJKqBA
Collaborator

Review pass — pushed one commit. The skill is the right thing to write; the problem is that
it was distilled from the Greece catalogue while that catalogue's central verdict was wrong,
so it propagated both the wrong numbers and the wrong lesson.

The numbers. It said Greece landed "five shared layers (mask, borders, cover, water)" —
which names four — and "seven absences" while naming six. It is seven shared layers and
four absences, because roads and railways are not absences (#179, #180, corrected
catalogue in #183).

The lesson. The gate this skill codifies is exactly the step that failed on Greece, so it
now carries the two rules that would have caught it:

  • A timeout is not a verdict. geodata.gov.gr is an AAAA-only host and the research box
    has no IPv6 route — it could only ever time out there. "The geoportal is offline" was a
    fact about the network path recorded as a fact about the source, and the same inference was
    applied to ktimanet.gr, which answers HTTP 200. The step names the checks now
    (getent ahostsv4/ahostsv6, ip -6 route show default, a second vantage, the Archive
    CDX API for dating a real outage).
  • Search the EU aggregator and the operators, not only the portal and the ministries.
    Every Greek transport source was indexed on data.europa.eu the whole time; a ministry-first
    search missed them because the publishers are state-owned operators under their own names
    (Egnatia Odos, GAIAOSE).

Plus two pitfalls worth carrying forward: an ABSENT verdict is never tested by anything
downstream the way a GO verdict is tested by the import that follows it, so absences deserve
the higher standard of evidence; and a PR body written against the first commit and never
updated
costs more trust than it saves — #177 shipped one saying its live download had not
run, hours after it had.

Also folded in from the #177 review: wire a provider into both fetch and list and update
the docs for both, and re-mint the session token rather than holding one for the whole fetch.

Review pass — pushed one commit. The skill is the right thing to write; the problem is that it was distilled from the Greece catalogue while that catalogue's central verdict was wrong, so it propagated both the wrong numbers and the wrong lesson. **The numbers.** It said Greece landed "five shared layers (mask, borders, cover, water)" — which names four — and "seven absences" while naming six. It is **seven** shared layers and **four** absences, because `roads` and `railways` are not absences (#179, #180, corrected catalogue in #183). **The lesson.** The gate this skill codifies is exactly the step that failed on Greece, so it now carries the two rules that would have caught it: - **A timeout is not a verdict.** `geodata.gov.gr` is an AAAA-only host and the research box has no IPv6 route — it could only ever time out there. "The geoportal is offline" was a fact about the network path recorded as a fact about the source, and the same inference was applied to `ktimanet.gr`, which answers HTTP 200. The step names the checks now (`getent ahostsv4`/`ahostsv6`, `ip -6 route show default`, a second vantage, the Archive CDX API for dating a real outage). - **Search the EU aggregator and the operators, not only the portal and the ministries.** Every Greek transport source was indexed on data.europa.eu the whole time; a ministry-first search missed them because the publishers are state-owned operators under their own names (Egnatia Odos, GAIAOSE). Plus two pitfalls worth carrying forward: an **ABSENT verdict is never tested by anything** downstream the way a GO verdict is tested by the import that follows it, so absences deserve the higher standard of evidence; and a **PR body written against the first commit and never updated** costs more trust than it saves — #177 shipped one saying its live download had not run, hours after it had. Also folded in from the #177 review: wire a provider into both `fetch` and `list` and update the docs for both, and re-mint the session token rather than holding one for the whole fetch.
The skill told the next country to verify the carve by checking that "0 km² of
background land overlaps the mask at the served zooms". That check cannot fail.
background_land is Natural Earth minus the union of every mask, so the
intersection is empty by construction — for a correct mask, for a land-only mask,
for an empty one. It passed on Greece while every Greek gulf was drawn as land.

Replaced with two checks that test whether the mask covers what it ought to: its
area against the country's land area (a maritime mask is materially larger —
Greece 167 843 vs 132 041 km², ratio 1.27, where a land-only mask scores 1.00),
and a named enclosed-water window that a land mask gets wrong (the Gulf of
Corinth) holding near-zero background land. Both sub-second. The rigorous version
times out at 100 s and catches nothing the cheap two miss.

The mask step now leads with the requirement instead of burying it: the mask must
be maritime, because an administrative division of the land stops at the shoreline
and Natural Earth does not resolve gulfs as water at 1:110 m. It points at the WFD
coastal and transitional water bodies as the source most coastal EU states publish,
and it says plainly that Iceland's buffered convex hull is only safe for a country
with no neighbours — a hull on Greece would have carved Natural Earth out from
under Albania, North Macedonia, Bulgaria and Anatolia.

Also: R34's reference is the country's land, not its mask, wherever the mask is
maritime or buffered — Iceland uses `mork` and Greece uses `gr_perifereies` for
the same reason, and against a maritime mask the check reads 21.9% off.

And a third pitfall to sit with the other two: a measurement is not a verdict.
Greece's mask area contradicted a config comment, and the contradiction was
written up as a comment inaccuracy rather than as the bug it was evidence of.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8HxnPejASTBhLaSxJKqBA
The skill told the next country to build a MARITIME mask so the Natural Earth
carve would not draw land in its gulfs. That was the Greece fix that did not work
— the Gulf of Corinth kept a kilometre-wide line through it — and it is no longer
the design. conf/world.yaml carves with Natural Earth's own admin-0 polygons and
reads only the country code from map_country_mask_0, never its geometry (#184,
#185). So the mask step now says: build it from the national boundary source and
stop; the carve is checked once for the world, not per country.

It also says, with the numbers, why extending the mask over the water is the wrong
answer — because it is the obvious answer and the next country will reach for it.

Three pitfalls added or sharpened, all of them things that cost real time here:

A bug reported against the country you are working on is not necessarily in it.
Greece's grey gulfs looked exactly like a Greek mask problem; the cause was a
world-level subtraction that had Sweden worse than Greece the whole time, and two
rounds of Greece-shaped fixes went by before anyone measured the other four.

A check that cannot fail is worse than no check, because it counts as coverage.
Two in this project could not: "no background land overlaps the mask" is true by
construction, and R145 as first written asked whether a neighbour kept the land
Natural Earth said was its own — agreeing with Natural Earth's opinion of the
border, which was the thing that was wrong.

And the measurement-is-not-a-verdict entry is corrected: the 132 041 km² number
was real evidence of a real bug, it just took two more attempts to place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8HxnPejASTBhLaSxJKqBA
art merged commit 0850ecc93a into main 2026-08-20 10:08:39 +00:00
art deleted branch add-country-skill 2026-08-20 10:08:39 +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!178
No description provided.