/** * @copyright Sister Software * @license AGPL-3.0 * @author Teffen Ellis, et al. * * Postcode regex repair pass — v0.7 task #35 ("postcode regex pre-pass"). * * The 2026-05-29 postcode diagnostic showed the neural model fragments alphanumeric postcodes at * the SentencePiece layer (GB/CA/NL at 0%, US 80.5%, FR 70.1%). Three failure modes were visible * in the data: * * 1. Total miss — "London SW1A 1AA" → (no postcode label) * 2. Truncation — "M5V 2T6" → "2T6"; "B12 8QX" → "B12" * 3. Char-drift — "75008" → "5008"; "62701" → "2701" (and smear: "1200-030 Lisboa" → "200-030 Lis") * * This pass runs AFTER the model's per-token BIO labels are decoded but BEFORE `buildAddressTree`. * It detects postcode-shaped substrings with per-country regexes and repairs the label sequence * so the postcode span matches the detected shape. The model is untouched — this is a * deterministic decoder-side correction, the "lowest risk" lever in the v0.7 plan (vs. #36's soft * FST shallow-fusion or #41's char-level encoder). * * PRECISION GUARDS (so we never regress the countries already passing): * * - Alphanumeric shapes (GB/CA/NL/DE-prefixed) are high-confidence "this IS a postcode" patterns → * eligible to ADD a span where the model emitted none, but only over non-structural labels * (never over house_number/street/etc.). * - Numeric shapes (\d{5}, ZIP+4, BR, JP, PT, PL) are ambiguous (a bare 5-digit could be a house number) * → SNAP-only: they expand/clip an EXISTING postcode span, never create one from scratch. * - Smear cleanup is LOCAL: only postcode tokens immediately flanking a snapped span are cleared. We * never globally clear unmatched postcode tokens — that would regress shapes we don't * pattern-match (AU 4-digit, IN 6-digit, …). * * A MISSING shape is not neutral for a HYPHENATED postcode. The local smear cleanup is local to a * MATCH, and an unlisted compound shape still matches at its numeric head: `NUM5` claims the five * digits of an unlisted `NNNNN-NNN`, snaps the span down to them, and clips the suffix the model * correctly labeled. That is how BR CEPs were truncated for the pass's whole life (#35, diagnosed * 2026-08-10) while the unhyphenated shapes above degraded gracefully. Before concluding a hyphenated * postcode is a model failure, re-parse it with `postcodeRepair: false`. */ import type { DecoderToken } from "@mailwoman/core/decoder"; import { type RepairResult, type SpanMatch } from "./span-repair.ts"; export type { RepairResult } from "./span-repair.ts"; /** * A detected postcode-shaped substring with its char range and confidence class. */ export interface PostcodeMatch extends SpanMatch { /** * "alnum" shapes may ADD; "numeric" shapes may only SNAP an existing span. */ kind: "alnum" | "numeric"; } /** * Per-country postcode shape patterns, ordered most-specific → least. Alphanumeric patterns require uppercase letters * (postcodes are conventionally uppercase, and the eval data has them uppercase) — this keeps them from matching * ordinary lowercase prose. */ export declare const POSTCODE_PATTERNS: Array<{ label: string; kind: "alnum" | "numeric"; re: RegExp; }>; /** * Collect non-overlapping postcode matches, preferring more-specific (earlier) patterns. */ export declare function collectMatches(text: string): PostcodeMatch[]; /** * Repair postcode label spans in a decoded token sequence using per-country regexes. Returns a NEW token array (inputs * are not mutated) plus a change count. */ export declare function repairPostcodeLabels(text: string, input: readonly DecoderToken[]): RepairResult; //# sourceMappingURL=postcode-repair.d.ts.map