Agent guide: contributing Internacia data
Platform-neutral workflow for editing country and intblock YAML safely. Works with Cursor, Claude Code, Copilot, Codex, and any coding agent in this repository.
Before editing
- Read CONTRIBUTING.md and country-code-policy.md.
- For consumers querying exported data, use ai-consumers.md — do not parse YAML unless authoring.
- Scope guardrail: countries are reference data only. Do not add HDI, GDP, government type, internet penetration, or similar socioeconomic profile fields.
Source layout
| Path | Rule |
|---|---|
data/countries/{CODE}.yaml | One file per entity; filename = ISO alpha-2 code |
data/intblocks/{category}/{ID}.yaml | Filename must match id; directory = primary blocktype; id unique globally |
data/blocktypes/blocktypes.yaml | Taxonomy; every intblock blocktype value must exist here |
data/datasets/ | Generated only — never hand-edit |
Countries checklist
- Required:
code,name,iso3code,numeric_code,entity_type,code_status - Non-ISO codes (
user_assigned,obsolete): document in country-code-policy.md; addrecognition_statuswhen needed population/area/gini: struct{value, year, source, source_id}— useyear: nullif unknown, neveryear: 0; values must be plausible (no negative population, gini in 0–100, no future years)borders: ISO alpha-3 neighbor codes (e.g.CAN,MEX); each must resolve to an existing country'siso3codeand should be reciprocal (neighbor lists you back) — exceptions go inborders.reciprocity_allowlistofcountries_completeness.yamlun_member/independentmust not betrueon dependent territories, SARs, or statistical areastldis.xx-style lowercase;calling_codesare+digits;timezonesmust be IANA tz names;flag_emojimust match the code's regional-indicator pair;landlocked: truerequires non-emptyborderscapital_citycoordinates must be plausible: the analyzer flags capitals beyond an area-scaled distance fromcentroid(CAPITAL_FAR_FROM_CENTROID) — a flag usually means swapped lat/lng; legit outliers go ingeography.capital_distance.allowlistsubregionmust belong to one of the record'scontinents; transcontinental exceptions go inregion_hierarchy.allowlistofcountries_completeness.yaml- Add
provenancewhen setting or updating enriched fields (aim for at least four entries per record — seeprovenance.min_countin completeness schemas; validation warns viaINSUFFICIENT_PROVENANCE) - Refresh via
python scripts/enrich_countries.py(see enrichment.md)
Intblocks checklist
- Required:
id,name,blocktype,status idis uppercase ASCII (official acronym; see intblock-inclusion-policy.md)includes[].idis authoritative for joins (country alpha-2);nameis display-onlyincludes[].statusmust be a key fromdata/schemas/includes_status.yaml- Records without
includesmust setmembership_applicability: not_applicablewhen membership is intentionally absent - Completeness priorities: see
data/schemas/intblocks_completeness.yaml(high: includes; medium: wikidata_id, non-templated description; low: languages, headquarters, regions, other_names, provenance, links).provenanceshould list at least four field-level entries (min_count: 4; warns viaINSUFFICIENT_PROVENANCE). - Dissolved orgs:
status: historical+dissolveddate; do not invent membership.founded/dissolvedmust be real dates (YYYY,YYYY-MM,YYYY-MM-DD, orYYYYs) — neverYYYY-00-00— anddissolvedmust not precedefounded predecessor/successor/suborganizations[].idmust resolve to an existing intblockidor alias; affiliated bodies without their own record go inreferences.org_ref_allowlistofintblocks_completeness.yamlheadquarters.countrymust resolve to adata/countries/{code}.yamlfilewikidata_idmust be unique across all records; intentionally shared concept Q-ids go inreferences.wikidata_duplicate_allowlistwith a reason commentmembership_count, when set, should match theincludescount (total or member-class entries)includes[].joined/leftmust be real dates,leftnot beforejoined, neither afterdissolved(joining beforefoundedis fine — ratification precedes entry into force)founding_membersentries must resolve to country codes and appear inincludes(useformer_memberfor founders that left)- Historical entity countries (e.g.
AN,SU) must not carry active statuses in non-historical blocks — useformer_member - Topic keys must exist in
data/schemas/topics.yaml; genuinely new keys are added to that catalog in the same PR - Lineage: when
predecessor/successorresolves to a record whose inverse field is empty, add the back-reference; a record listed in a parent'ssuborganizationsmust declare that parent inpartof - Shared English acronyms across same-blocktype records flag
DUPLICATE_ACRONYM; real-world collisions go inreferences.acronym_duplicate_allowlist - Quote YAML boolean lookalikes:
'NO'(Norway),'no'(Norwegian) - New blocktype: add to
data/blocktypes/blocktypes.yamlfirst
Validate before PR
python scripts/validate_countries.py
python scripts/validate_intblocks.py
python scripts/validate_countries.py --json # structured output for agents; exit 0 = no errors, 1 = errors
python scripts/validate_intblocks.py --json
# --fail-on-warning: treat warnings as errors (exit 1). Warnings alone do not fail.
pytest tests/
ruff check internacia_builder/ scripts/ tests/
python scripts/builder.py build --formats parquet,duckdb
CI mirrors .github/workflows/validate.yml.
Schema or breaking changes
New capabilities, schema changes, or breaking exports require an OpenSpec proposal first:
- Read openspec-quickstart.md or full openspec/AGENTS.md
- Scaffold
openspec/changes/<change-id>/withproposal.md,tasks.md, spec deltas - Run
openspec validate <change-id> --strict - Do not implement until approved
Update CHANGELOG.md under [Unreleased] for consumer-visible changes.
Common fixes
| Mistake | Fix |
|---|---|
Alpha-2 in borders | Use alpha-3 (iso3code of neighbor) |
Plain number for population | Use struct with value, source |
| Intblock id collision | Pick new unique id; add alias in intblocks_aliases only via maintainer workflow |
Invalid blocktype | Add entry to blocktypes.yaml, re-validate |
| Missing provenance on enrichment | Append {field, source, retrieved_at} to provenance list |
Missing includes on non-membership record | Set membership_applicability: not_applicable |
Related
- AGENTS.md — root routing hub
- AGENTS.zh.md — 中文路由入口
- query.md — querying exported data
- add-intblock-example.md — worked add-a-record walkthrough
- zh/contribute.md — 中文编辑指南