Labelled account-number rule contract
Status: 0.5.0-beta.2 draft; not released. This is the account-number slice of D2 / #144. Masked account values have a separate format contract; street addresses remain separately scoped. The synthetic cases infixtures/account-number.jsonl are rule acceptance cases and reusable
launch fixtures, not model qualification results.
Supported text
The canonical type isACCOUNT_NUMBER, with value-only spans and
datafog-core/account-number rule provenance. It runs in all locales by
default and honors ordinary scan selection, transformation selection and allowlists.
Recognize ASCII-case-insensitive account number, account no., acct number
and acct no. labels. The period on no. is required in this first slice.
A preceding bank is ordinary surrounding text, not a distinct required label.
Words within the label require horizontal spacing. Between label and value,
require horizontal spacing or a single colon/hash with optional horizontal
spacing. Horizontal spacing means space, tab, nonbreaking space or narrow
nonbreaking space; it never crosses a newline.
The value is 6–17 contiguous ASCII digits, including leading zeros. This is
a deliberately bounded initial lexical format, not bank validation or a claim
that all account numbers have this length. It covers an eight-digit UK-style
synthetic example and longer synthetic identifiers while avoiding short numeric
references. Four/five-digit, longer, alphanumeric, international, hyphenated and
space-grouped accounts are outside this first rule; a configured model may still
find them. Checksums, account existence and institution ownership are not checked.
No real account numbers appear in these fixtures.
Use Unicode word boundaries around the label and value, rejecting adjacent
letters, digits, underscores or combining marks. Do not accept a prefix of a
longer run, decimal, date or split numeric value: a digit after a following
period/comma/slash/hyphen or horizontal spacing disqualifies the whole candidate.
Terminal punctuation or punctuation followed by prose is permitted.
Exclusions and rationale
- Bare numbers,
reference,order,invoice,customer ID, genericaccount:andacct:have insufficient context for this slice. - Intervening prose (
account number is ...) and line breaks separate the label from its value and are rejected. - Negative numbers, date/decimal fragments and partially matched digit groups are rejected; digits already matched by PHONE, SSN or CREDIT_CARD are not globally suppressed or reclassified. A labelled valid card still yields its CREDIT_CARD finding. Existing equal-span selection chooses ACCOUNT_NUMBER for a default transformation; selecting CREDIT_CARD keeps card attribution. This intentional new overlap is covered by a regression, not a new source-precedence rule.
- Masked accounts follow the separate masked-value contract; bare endings or masked tokens still lack account context.
- JSON field keys do not supply text context; scan each decoded string as today.
Model compatibility
ACCOUNT_NUMBER is explicitly partial in the internal detector registry. It
adds rule findings without replacing configured model coverage, for omitted and
explicit entity selection alike. Native and custom models still execute for
accounts, including formats this lexical rule excludes. Existing entity routing
for EMAIL, PHONE and the other rule types is unchanged.
Equal-span, equal-type findings use current duplicate selection and keep both
sources of evidence. Different spans remain available to the existing
transformation selection policy; neither source gets blanket precedence. Raw
scan_model behavior is unchanged. This internal compatibility distinction does
not add the caller-visible engine routing controls tracked in
#126. Contract regressions
protect model admission and wider coverage without treating synthetic backend
spans as real model qualification.
Verification
Run doctor, the rule precision/recall checks, installed SDK smoke and live parity; this corpus runs in the Core and installed SDK conformance paths. Keep the existing corpus unchanged unless an intentional coverage change is reviewed. Measure binary and latency impact before making performance claims. This slice leaves #144 open; street-address coverage remains separately scoped.Local impact measurement
On macOS ARM64 with Node 24.18.0, 49 short synthetic fixture texts scanned 1,000 times each after warmup averaged 0.68 µs before and 1.06 µs after this addition. This includes returning the new account findings and is an uncontrolled local measurement, not a stable benchmark claim. The release WASM grew from 2,452,055 to 2,458,647 bytes (+6,592 bytes, about 0.27%). The ordinarycargo dfv bench
report remains the separate performance gate.