Skip to main content

Component Schema

This document defines the canonical ComponentTag union. It is the single source of truth.

Rule: any change to this file requires:

  1. A written rationale in the commit message.
  2. A migration plan for existing corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. rows tagged with the old schema.
  3. A check that downstream alignmentalignmentThe step in the corpus pipeline that takes a (raw, components) pair from an adapter and produces a (raw, tokens, BIO labels) row by finding each component's text inside the raw string and labeling the matching tokens., trainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input., and inferenceinferenceRunning the trained model on new input to get predictions, as opposed to training, which produces the model. In Mailwoman that means a small transformer encoder reads an address string and classifies every token — house number, street, locality, region, postcode, and the rest. A Who's On First gazetteer can feed soft location hints into the pass, but the model makes the final call on every label. Where a generative model writes text token by token, Mailwoman's output is a retrieval-augmented token classification: one label per input piece. code is updated in the same commit.

Tag inventory

Universal (Phase 1, all locales)

TagDescriptionExample
countrySovereign stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. name or codeUSA, France, FR
regionFirst-level admin (stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality., région)OR, Île-de-France
localityCitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy., town, communePortland, Paris
dependent_localitySub-localitydependent localityA sub-locality (neighbourhood or borough) hierarchically inside a larger locality — e.g. Brooklyn within New York City. Provides finer geographic specification below the primary locality. (neighborhood, arrondissement, ward)Brooklyn, 8e arrondissement
postcodePostal code97215, 75008
subregionOptional county-level admin between regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. and localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.Multnomah County

Street-level (Phase 2)

TagDescriptionExample
house_numberBuilding number on a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.6220, 12bis
streetStreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. name properSalmon St, République
street_prefixDirectional or descriptive prefix (Anglophone)SE, North
street_prefix_particleNon-English grammatical particle (FR)de la, du, des
street_suffixStreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. type suffix (Anglophone)Street, Boulevard, Ave
intersection_aFirst streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. of an intersectionintersectionAn address that names a location by two crossing streets ('5th & Main') rather than a number and street. Mailwoman tags the two streets as intersection_a and intersection_b — a negative-space format that starved the early model. query5th Ave (in "5th Ave & 42nd St")
intersection_bSecond streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. of an intersectionintersectionAn address that names a location by two crossing streets ('5th & Main') rather than a number and street. Mailwoman tags the two streets as intersection_a and intersection_b — a negative-space format that starved the early model. query42nd St (in "5th Ave & 42nd St")
unitApartment, suite, floorApt 4B, Suite 200, 5e étage

Venue-level (Phase 3)

TagDescriptionExample
venueNamed placevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. (business, landmark, park)Mt Tabor Park, Eiffel Tower
attention"AttentionattentionThe core mechanism inside a transformer encoder. Each token's representation is updated by looking at every other token, with learned weights deciding how much each one matters." or "care of" linec/o Jane Doe, Att: Sales Dept
po_boxPost office boxPO boxA numbered mailbox at a post office used as a delivery address instead of a physical street location. Mailwoman tags it as the po_box component; structurally the same family as a subpremise.PO Box 1234, BP 42

Locale-specific

TagLocalelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for.DescriptionExample
cedexFRSpecial postal routing designationCEDEX 08 in 75008 PARIS CEDEX 08

JP-specific (Phase 6 — listed for forward compatibility, not used in Phase 1–3)

TagDescriptionExample
prefectureJP first-level admin (都道府県)東京都, Tokyo
municipalityJP citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy./ward (市区町村)千代田区, Chiyoda
districtJP district (大字)丸の内, Marunouchi
blockJP chōmechōmeIn Japanese addressing, a district-level subdivision in the block-based chōme / banchi / gō numbering scheme, which uses area-and-block numbers instead of street names. (丁目)1丁目
sub_blockJP banchibanchiIn Japanese addressing, a block within a chōme. The middle level of the chōme / banchi / gō hierarchy. (番地)1番地
building_numberJP In Japanese addressing, a building or lot number within a banchi — the finest level of the chōme / banchi / gō block-based hierarchy. (号)1号
building_nameJP named building (often in romaji)Tokyo Building

Note for JP-forward-compatibility: the JP-specific tags above must not be referenced anywhere in core code in PhasesphaseA milestone in the implementation plan (Foundation, Corpus, Training, Integration, and forward-looking phases). Distinct from stage (runtime pipeline) and tier (model vocabulary). 0–5. They exist in this document so that schema additions in PhasephaseA milestone in the implementation plan (Foundation, Corpus, Training, Integration, and forward-looking phases). Distinct from stage (runtime pipeline) and tier (model vocabulary). 6 do not require a core rewrite. The componentsSupported field on LocaleProfile is how the system knows which tags a localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. actually uses.

BIO labeling

For trainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input. and inferenceinferenceRunning the trained model on new input to get predictions, as opposed to training, which produces the model. In Mailwoman that means a small transformer encoder reads an address string and classifies every token — house number, street, locality, region, postcode, and the rest. A Who's On First gazetteer can feed soft location hints into the pass, but the model makes the final call on every label. Where a generative model writes text token by token, Mailwoman's output is a retrieval-augmented token classification: one label per input piece., each tag T becomes two BIO labels:

  • B-T — beginning of a spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. tagged T
  • I-T — inside (continuation) of a spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. tagged T

Plus one universal labelcomponent tagOne of the 33 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag.:

  • O — outside any address component (punctuation, noise, junk)

Example labeling of "6220 SE Salmon St, Portland OR":

Token Label
───── ─────
6220 B-house_number
SE B-street_prefix
Salmon B-street
St I-street
, O
Portland B-locality
OR B-region

Implementation notes

TypeScript representation

// packages/core/src/types/component.ts

export const COMPONENT_TAGS = [
// Universal
"country",
"region",
"locality",
"dependent_locality",
"postcode",
"subregion",
// Street-level
"house_number",
"street",
"street_prefix",
"street_prefix_particle",
"street_suffix",
"intersection_a",
"intersection_b",
"unit",
// Venue-level
"venue",
"attention",
"po_box",
// FR-specific
"cedex",
// JP-specific (Phase 6 — declared but unused until then)
"prefecture",
"municipality",
"district",
"block",
"sub_block",
"building_number",
"building_name",
] as const

export type ComponentTag = (typeof COMPONENT_TAGS)[number]

export const BIO_LABELS = ["O", ...COMPONENT_TAGS.flatMap((t) => [`B-${t}`, `I-${t}`])] as const

export type BioLabel = (typeof BIO_LABELS)[number]

The as const and derived types are deliberate. TypeScript will surface schema-aware errors at compile time wherever a tag is referenced.

Validation rule

A LocaleProfile.componentsSupported array must be a subset of COMPONENT_TAGS. Runtime check at profile registration. Fail loudly if violated.

Rationale for specific choices

Why dependent_locality and not neighborhood or borough? WOFWOF (Who's On First). An open-source gazetteer of places maintained by Mapzen/whosonfirst. Mailwoman builds a custom SQLite database from WOF GeoJSON repos, extended with postcode data, importance scores, and coincident-role relations. and ISO use dependent_locality for the general concept. Names like borough are localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for.-specific. Pick the umbrella term.

Why split street_prefix from street_prefix_particle? English SE and French de la are grammatically different and synthesis pipelinesstaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. need to treat them differently. Conflating them produces worse trainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input. data.

Why expose subregion if it's optional? Some US addresses include county (rare in display but common in government data). Modeling it explicitly is better than forcing it into region or locality.

Why cedex is FR-specific and not subsumed by postcode? A CEDEXCEDEX (Courrier d'Entreprise à Distribution Exceptionnelle). A French postal routing for high-volume business mail: a CEDEX code delivers directly from a sorting centre, bypassing the local post office. A common negative-space format Mailwoman must parse. designation is a postal routing instruction, not a postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one.. Treating it as one corrupts FR postal code statistics.

Why list JP tags here at all before PhasephaseA milestone in the implementation plan (Foundation, Corpus, Training, Integration, and forward-looking phases). Distinct from stage (runtime pipeline) and tier (model vocabulary). 6? Forces Phase 0 type design to handle them. If core code reaches PhasephaseA milestone in the implementation plan (Foundation, Corpus, Training, Integration, and forward-looking phases). Distinct from stage (runtime pipeline) and tier (model vocabulary). 6 and needs to add seven new tags plus rewrite the policy system, the schema-first principle failed.