Skip to content

Add Transport Canada, build registry sources in parallel, and join them into one asset - #72

Draft
anchildress1 wants to merge 11 commits into
PlaneQuery:developfrom
anchildress1:feat/parallel-registry-build
Draft

Add Transport Canada, build registry sources in parallel, and join them into one asset#72
anchildress1 wants to merge 11 commits into
PlaneQuery:developfrom
anchildress1:feat/parallel-registry-build

Conversation

@anchildress1

@anchildress1 anchildress1 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Draft. 11 atomic commits, 8 files. Each commit builds and imports cleanly at its own SHA.

Two related changes that only make sense together: a second registry source, and a pipeline shape
that can actually hold more than one.


1. Transport Canada as a registry source

Picked against two constraints — the licence must permit redistribution in a public release, and the
data must carry ICAO 24-bit hex, since the ADS-B pipeline joins on it.

Most redistributable registries fail the second test: Australia, Brazil, Spain, Singapore and
Lithuania publish no hex at all. Taiwan, Estonia and Chile are cleared only by bilateral permission
granted to a different project, which does not travel here. Switzerland has hex but the same
bilateral problem. Transport Canada satisfies both — a standing public open licence, and 100% Mode
S coverage across all 34,913 records
.

Two details worth a close look

  • Registrations have two eras. Three-character marks are vintage CF-; everything else takes the
    modern C- prefix. The split is 2,451 / 32,462.
  • ACTIVE_FLAG does not mean "current owner". 1,932 currently-Registered marks carry only I
    parties, and those rows are the MAIL_RECIPIENT. Filtering on A alone publishes registered
    aircraft with no owner at all, so the code prefers A and falls back to all parties.

Owner mailing addresses are published, matching the registrant_* addresses the FAA asset already
carries for 99.7% of US registrants. Because CCARCS lists one row per party, the address comes from
the single MAIL_RECIPIENT row (exactly one per mark) rather than being merged across co-owners —
a co-owned mark otherwise produces two streets and two cities with no way to pair them.

Column naming

Columns use the FAA vocabulary rather than CCARCS's own: registrant_* not owner_*, status not
registration_status, and source = "TC" matching the discriminator the FAA frame already carries.
That raises the column names shared with the FAA frame from 9 to 21, which is what makes part 2
possible. registrant_zip_code holds the Canadian postal code — the name is the FAA's on purpose,
because a union table wants one column per concept, not one per country's vocabulary.


2. Parallel source threads, joined once

Before this, every source was a hand-written job: adding one meant copy-pasting a job and editing
create-release in four places, and each source published its own asset — so a consumer looking up
one aircraft had to know which country's file to open, which is exactly what openairframes_id and
transponder_code_hex exist to avoid.

build-registry-source  (matrix: faa required, tc tolerated)   parallel threads
        |  each calls the reusable registry-source.yaml
        v
join-registry          waits for all, merges on the union of columns
        v
create-release

Adding a registry is now one matrix entry plus src/create_daily_<source>_release.py. The
reusable workflow derives the script path from the source id and fails loudly if it is missing;
join-registry discovers sources by artifact pattern, so nothing downstream changes.
required: true|false per matrix entry replaces ad-hoc continue-on-error, so whether a source can
block the release is declared in one place instead of inferred from several.

New asset

openairframes_registry_{start}_{end}.csv — every registry source on the union of columns,
discriminated by source. Each frame is reindexed to the union before concatenating, so a source
missing a column yields an empty cell rather than a shifted row.

openairframes_faa_*.csv keeps publishing unchanged. It is documented in the README and people
pull it, so it should be deprecated deliberately rather than disappearing under them.

Verified on real data

Joined the live 275 MB FAA asset with a live CCARCS build:

faa: 625,257 rows x 50 cols
tc:   34,913 rows x 41 cols
->   660,170 rows x 70 cols
  • 100% coverage of transponder_code_hex, registration_number and openairframes_id on both sources
  • shared fields populate on both sides (registrant_name FAA 96.7% / TC 100%)
  • zero hex collisions between sources

NOTICE

LICENSE covers code; released data has separate terms. Canada requires two notices to reach the
consumer together, so NOTICE carries them and is validated as a required release file —
shipping the data without it removes the permission that makes it redistributable. It also had to be
added to the create-release sparse checkout, which otherwise checks out only .github.

NOTICE also records that the derived openairframes_faa_*.csv is not itself a US government work:
17 U.S.C. §105 disclaims copyright in the FAA's own data and says nothing about a derivative.

Also here

AGENTS.md is trimmed to operative constraints only (151 lines to 85) and updated for the above.

Happy to split any of this back out if you would rather take the pipeline change separately.

🤖 Generated with Claude Code

- carry the two Government of Canada notices the open licence requires verbatim
- record FAA public-domain provenance and unassessed third-party republished assets
- LICENSE covers code only; release data carries its own conditions

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- parse the headerless latin1 CCARCS export against its declared column layout
- derive transponder_code_hex from the 24-bit Mode S binary, populated for all 34,913 rows
- expand marks to C- and vintage CF- registrations and drop owner mailing addresses
- mirror the FAA build: same concat-with-latest-release dedup and output conventions

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- attribution is a licence condition and NOTICE must travel with release assets
- a public licence travels to this project; a bilateral permission does not
- non-commercial-only registries are incompatible with the MIT-licensed releases

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- reject Mode S fields that are not 24 binary digits; a short field zero-padded into a
  plausible address belonging to a different aircraft
- check the "N rows selected." footer against the parsed row count and enforce a row floor,
  so an upstream short export fails instead of publishing as a smaller register
- prefer ACTIVE_FLAG "A" parties but fall back to all: 1,932 Registered marks carry only "I"
  rows, and those are the MAIL_RECIPIENT, so filtering on "A" alone drops real owners
- count distinct owner names rather than rows, fixing 154 marks labelled Co-owner in error
- match the spool footer by pattern instead of a brittle ragged-row count

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- fall back to a single-day rebuild only on FileNotFoundError; a rate limit, schema change
  or truncated download previously took the same path and republished one day as the whole
  dataset, which the next run then read back as its base
- move the monotonic download_date assert out of the try so corruption cannot select the
  destructive branch
- walk back through releases like the ADS-B reader, so one missing optional asset does not
  strand the accumulation
- authenticate release reads and verify downloaded asset size
- read the previous CSV with keep_default_na=False so literal NA values round-trip
- write the download atomically and reject non-zip responses

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- stop asserting US public-domain status for the derived FAA CSV; section 105 covers the
  government's own work, not this repository's derivative
- name no licence instrument for Transport Canada, which does not publish one on the
  download page, rather than citing one that cannot be verified
- state that MIT covers code only, resolving a contradiction with AGENTS.md
- lead the source-eligibility rule with the bilateral-permission bar; Taiwan is OGDL
  licensed and excluded for that reason, not for commercial terms

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- publish street, city, postal code and care-of, which the FAA asset already carries as
  registrant_* for 99.7% of US registrants; dropping them here left one repository with
  two different postures on the same class of data
- take the address from the single MAIL_RECIPIENT row rather than merging across parties,
  since a co-owned mark lists several people in different cities

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- registrant_* rather than owner_*, status rather than registration_status, so both
  registries describe the same concept with the same column name
- registrant_zip_code carries the Canadian postal code: a union table needs one column per
  concept, not one per country's vocabulary
- set source="TC", matching the discriminator the FAA frame already carries
- raises the column names shared with the FAA frame from 9 to 21

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- drop narrative rationale, restated rules and background a reader can grep for
- one rule per line; the file is read by a model about to act, not by a person catching up
- 151 lines to 85

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- add a reusable registry-source workflow so every source runs in parallel rather than as
  a hand-written job; adding a registry becomes one matrix entry plus one script
- add build_registry.py to align the per-source CSVs on the union of columns and emit a
  single table discriminated by the source column
- reindex each frame to the union before concatenating, so a source missing a column
  yields an empty cell rather than a shifted row

Generated-by: Claude Opus 5 <noreply@anthropic.com>
- replace the hand-written build-faa job with a matrixed call per source, faa required and
  tc tolerated, joined once every thread has finished
- publish openairframes_registry_*.csv and keep openairframes_faa_*.csv during transition
- validate the joined registry and NOTICE as required release files
- report missing optional assets as a workflow annotation rather than a plain echo

Generated-by: Claude Opus 5 <noreply@anthropic.com>
@anchildress1 anchildress1 changed the title Build each registry source in parallel and join into one registry asset Add Transport Canada, build registry sources in parallel, and join them into one asset Sep 2, 2026
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.

1 participant