Component Schema
This document defines the canonical ComponentTag union. It is the single source of truth.
Rule: any change to this file requires:
- A written rationale in the commit message.
- 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.
- 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)
| Tag | Description | Example |
|---|---|---|
country | Sovereign stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. name or code | USA, France, FR |
region | First-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 |
locality | CitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy., town, commune | Portland, Paris |
dependent_locality | Sub-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 |
postcode | Postal code | 97215, 75008 |
subregion | Optional 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)
| Tag | Description | Example |
|---|---|---|
house_number | Building 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 |
street | StreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. name proper | Salmon St, République |
street_prefix | Directional or descriptive prefix (Anglophone) | SE, North |
street_prefix_particle | Non-English grammatical particle (FR) | de la, du, des |
street_suffix | StreetstreetThe 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_a | First 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. query | 5th Ave (in "5th Ave & 42nd St") |
intersection_b | Second 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. query | 42nd St (in "5th Ave & 42nd St") |
unit | Apartment, suite, floor | Apt 4B, Suite 200, 5e étage |
Venue-level (Phase 3)
| Tag | Description | Example |
|---|---|---|
venue | Named 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" line | c/o Jane Doe, Att: Sales Dept |
po_box | Post 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
| Tag | LocalelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. | Description | Example |
|---|---|---|---|
cedex | FR | Special postal routing designation | CEDEX 08 in 75008 PARIS CEDEX 08 |
JP-specific (Phase 6 — listed for forward compatibility, not used in Phase 1–3)
| Tag | Description | Example |
|---|---|---|
prefecture | JP first-level admin (都道府県) | 東京都, Tokyo |
municipality | JP citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy./ward (市区町村) | 千代田区, Chiyoda |
district | JP district (大字) | 丸の内, Marunouchi |
block | JP 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_block | JP banchibanchiIn Japanese addressing, a block within a chōme. The middle level of the chōme / banchi / gō hierarchy. (番地) | 1番地 |
building_number | JP gōgō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_name | JP 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. taggedTI-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. taggedT
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.