- Guides
Order IDs and email addresses over a phone call
Preserve literal IDs and email characters with explicit uncertainty, safe subset boundaries, immutable snapshots, and Python fixtures.
- Author
Udit Goenka- Editorial date
- Reading time
- 25 min

TL;DR: Capture order IDs and email addresses as literal character sequences with explicit uncertainty. Preserve case and punctuation under the destination contract, keep email local-part handling separate from domain normalization, and distinguish a supported candidate from deliverability, ownership, or account-match evidence.
A caller spells an identifier. The recognizer produces a plausible word, a cleanup routine removes punctuation, and the account lookup finds a different record. Every component can appear to have helped while the actual character sequence changes between the caller and the consuming system.
Start with the field's literal contract. The phone-number preservation guide and postal-code guide show why a digit sequence is not automatically a number. Order IDs and email addresses add case, punctuation, spelling, and provider-specific semantics to that same preservation problem.
This guide provides a small standard-library reference for immutable grouped capture and an explicitly limited ASCII email subset. It does not recognize speech, validate every possible email syntax, send messages, authenticate callers, or look up accounts. Its printed output is synthetic evidence about character handling, with downstream delivery and ownership explicitly untested.
Table of contents
- Choose a literal field contract
- Keep groups and uncertain positions
- Preserve email local-part semantics
- Declare an email subset honestly
- Avoid normalization that invents identity
- Run the complete literal-capture reference
- Interpret the rejection and ownership fixtures
- Design a repairable spoken readback
- Invalidate approval after material repairs
- Separate syntax from downstream evidence
- Test the actual transfer boundaries
- Choose a capture acceptance matrix
- Keep repair ownership explicit
- Review the literal a reader actually receives
- Frequently asked questions
- Ship an inspectable character boundary
Choose a literal field contract
An order ID is interpreted by the system that issued it. A visible sequence can contain significant leading zeroes, case, hyphens, underscores, or other characters. A generic cleanup rule cannot know which differences the destination treats as meaningful. Obtain the actual supported field contract before normalizing input.
The reference defines a teaching order-ID subset: an ASCII letter or digit followed by supported ASCII letters, digits, hyphens, or underscores, within a fixed length. It preserves every accepted character exactly. This is a chosen example grammar, not a claim about every order system's identifier format.
The money and quantity guide explains why choosing field meaning comes before numeric conversion. An order ID containing only digits still identifies something rather than requesting arithmetic. Do not strip zeroes or infer a quantity merely because a transcript contains a numeric-looking token.
Email has its own public standards and destination behavior. It should not share the order-ID grammar or inherit an arbitrary case-folding policy. Keep the field kind explicit at the capture boundary, so a plausible string cannot silently move from one interpretation to another.
The function returns a supported candidate, a clarification disposition, or an unsupported disposition. Invalid API metadata raises a visible error. Those distinctions prevent a missing field or unsupported representation from becoming an empty string that a later component mistakes for a successfully captured identifier.

Ask for the field in a way that matches its purpose. A caller expecting to supply an order identifier should not be prompted for an account password or authentication PIN. Capturing a literal value and proving permission to access the associated record are separate operations, even if a particular workflow requests them during the same call.
Keep groups and uncertain positions
Grouping can make spelling repairable. Retain the sequence of confirmed or proposed groups as well as the joined literal. The reference joins groups without inserting or removing characters; punctuation must be present explicitly in a group. That rule avoids turning a display pause into an unapproved separator.
A group list passed by a caller is mutable, so the function copies it into a tuple. Strings within it are immutable. The returned capture record is frozen, and uncertainty positions are copied into another tuple. Ordinary changes to the caller's original list cannot rewrite a result that another component has already inspected.
An uncertain position should remain uncertainty rather than an invented character. The synthetic letter-O and zero fixture keeps the literal distinction visible, then supplies uncertainty metadata for one position. The function returns clarification with no canonical candidate, even though the visible characters would otherwise fit the example order-ID grammar.
The self-correction guide discusses why a later repair can replace an earlier interpretation. Keeping groups and positions makes that repair targeted: ask about the disputed character or group instead of forcing the caller to repeat an entire long address every time.
Validate uncertainty metadata before deduplicating or sorting it. The reference rejects booleans, strings, negative positions, out-of-range positions, and duplicates. A malformed position is not silently merged with a legitimate integer or dropped because the candidate happens to parse successfully.
The reference uses character offsets in its joined Python string. Its supported canonical subset is ASCII, so accepted characters also have straightforward byte representations. A system supporting broader Unicode must define whether its repair positions identify code points, grapheme clusters, or another unit; this artifact makes no such expanded indexing claim.

Preserve email local-part semantics
RFC 5321 distinguishes the local part from the domain and assigns local-part interpretation to the destination host. Preserve its case rather than assuming every provider ignores it. Domain case can be normalized separately in this ASCII teaching subset.
The reference retains the entire original address and builds a candidate with only the ASCII domain lowercased. The synthetic fixture asserts that the local-part case remains intact. It also asserts that a different local-part case produces a different candidate under this capture policy.
A plus sign is literal input here. Some providers implement addressing conventions involving plus tags, but a capture function must not globally strip them or assume the same semantics everywhere. The reference keeps the plus and following characters exactly rather than trying to identify a provider's preferred account key.
The shared-phone identity guide explains a broader identity boundary: a contact attribute is not automatically a person. Even a perfectly captured address can belong to a shared mailbox or another person. Preserve the literal while obtaining separate authority for any account access or personalized disclosure.
Do not apply a familiar provider's rules to every address. Removing periods, rewriting aliases, or changing local-part case can merge values that the current destination treats differently. If a destination explicitly defines an account-match normalization, implement that as a separate named lookup contract while retaining the original captured address.
This distinction also helps debugging. A caller may approve the literal address while an account service uses another stable identifier internally. Keeping capture and lookup fields separate lets an engineer explain which transformation belongs to the destination rather than blaming speech recognition for a later identity merge.
Declare an email subset honestly
The reference accepts an unquoted ASCII dot-atom local part and a bounded ASCII domain with at least two labels. It checks label edges, local-part length, total literal length, and supported punctuation. Its candidate result means only that the address fits this chosen subset.
It does not claim full RFC email validation. Quoted local parts, address literals, internationalized local parts, and other unsupported forms need a separate supported path. A rejected subset input should be described as unsupported here, not globally invalid as an email address.
RFC 6531 specifies the SMTPUTF8 extension for internationalized email. Its existence is a reason to avoid treating ASCII-only code as universal validity authority. A deployment supporting internationalized addresses needs explicit transport capability and parsing contracts beyond this teaching reference.
A historical developer question about obtaining email addresses through speech asks whether character-by-character spelling is appropriate. The question provides useful task context; its old service-specific answers do not establish current recognition accuracy or a universal repair algorithm. This article makes no such performance claim.
The spelled-input turn-detection guide explains why a pause between characters need not finish the input. A literal capture contract can be correct while an end-of-turn policy still cuts the caller off before the domain or final group arrives.
Python's regular-expression documentation supports explicit character classes and fullmatch for a declared grammar. A regex is useful at this narrow boundary because its purpose is visible. It becomes misleading when a simple match is advertised as proof of deliverability, global validity, or ownership.

Avoid normalization that invents identity
Unicode normalization and confusable detection solve different problems from literal capture. Unicode's normalization specification defines canonical and compatibility forms. A transformation can be appropriate under one identifier contract and destructive under another, so it must be explicitly authorized by the destination semantics.
The reference performs no Unicode normalization. Non-ASCII text receives an unsupported disposition with the original string retained. That is a limit of the teaching subset, not a declaration that the caller's name, writing system, or email is invalid.
Unicode's security mechanisms specification describes confusables and identifier-related mechanisms. Detecting that characters may be confused should produce evidence for review or clarification, not silently authorize replacing one literal with another. Similar appearance does not establish identical account identity.
The mixed-script TTS guide addresses a related presentation problem. A pronunciation surface can help a caller hear an identifier, but it must remain distinct from the authoritative character string. Rewriting a surface for speech must not mutate the value eventually submitted to a lookup adapter.
Do not turn a recognition hint into an authoritative character substitution. A model may suggest that a common domain was intended; the caller may actually have supplied a less familiar domain. The correct next step is targeted confirmation of the disputed component, rather than automatic replacement justified by popularity.
Keep unsupported syntax visible. If a quoted or internationalized address cannot pass the current route, offer a documented alternate capture path that supports it. Repeating the same ASCII-only prompt indefinitely is not a meaningful repair, and replacing the input with a nearby ASCII spelling changes the identifier.
Run the complete literal-capture reference
Run the full fence with Python 3.10 or later. It includes the capture function, immutable result, supported-subset checks, negative metadata fixtures, uncertainty behavior, and a JSON round trip. All example addresses use a reserved documentation domain, and no network operation occurs.
RFC 2606 reserves example domains for documentation. This makes the synthetic spelling useful without borrowing a real person's mailbox. A reserved example address still does not prove message delivery, and the code deliberately sends nothing.
from dataclasses import dataclass, FrozenInstanceError
import json
import re
@dataclass(frozen=True)
class Capture:
kind: str
groups: tuple
original: str
uncertain: tuple
canonical: str | None
disposition: str
def capture_literal(kind, groups, uncertain=()):
if kind not in ('order-id', 'email'):
raise ValueError('explicit supported field kind required')
if (not isinstance(groups, (list, tuple)) or not 1 <= len(groups) <= 32 or
any(not isinstance(g, str) or not g for g in groups)):
raise ValueError('nonempty bounded string groups required')
owned = tuple(groups)
original = ''.join(owned)
if not 1 <= len(original) <= 254:
raise ValueError('bounded literal required')
if (not isinstance(uncertain, (list, tuple)) or
any(type(i) is not int or not 0 <= i < len(original) for i in uncertain)):
raise ValueError('valid uncertainty positions required')
positions = tuple(uncertain)
if len(set(positions)) != len(positions):
raise ValueError('duplicate uncertainty position')
if positions:
return Capture(kind, owned, original, positions, None, 'clarify')
if not original.isascii():
return Capture(kind, owned, original, positions, None, 'unsupported')
canonical = None
if kind == 'order-id':
if re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9_-]{0,31}', original):
canonical = original
elif original.count('@') == 1:
local, domain = original.split('@')
atom = r"[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+"
local_ok = bool(re.fullmatch(atom + r'(?:\.' + atom + r')*', local))
labels = domain.split('.')
domain_ok = (len(labels) >= 2 and len(domain) <= 253 and
all(re.fullmatch(r'[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?', s)
for s in labels))
if local_ok and len(local) <= 64 and domain_ok:
canonical = local + '@' + domain.lower()
return Capture(kind, owned, original, positions, canonical,
'candidate' if canonical is not None else 'unsupported')
def rejects(kind, groups, uncertain=()):
try:
capture_literal(kind, groups, uncertain)
except ValueError:
return
raise AssertionError(('unexpected acceptance', kind, groups, uncertain))
groups = ['AB', '-', '00']
saved = capture_literal('order-id', groups)
groups[0] = 'ZZ'
assert saved.groups == ('AB', '-', '00') and saved.canonical == 'AB-00'
assert capture_literal('order-id', ['ab-00']).canonical == 'ab-00'
assert capture_literal('order-id', ['AB-00']).canonical != 'ab-00'
assert capture_literal('order-id', ['O0']).canonical == 'O0'
assert capture_literal('order-id', ['AB_00']).canonical == 'AB_00'
assert capture_literal('order-id', ['A'] * 32).disposition == 'candidate'
rejects('order-id', ['A'] * 33)
positions = [0]
uncertain_saved = capture_literal('order-id', ['O0'], positions)
positions[0] = 1
assert uncertain_saved.uncertain == (0,) and uncertain_saved.canonical is None
assert capture_literal('order-id', ['O0'], [0]).disposition == 'clarify'
assert capture_literal('order-id', ['O0'], [0]).canonical is None
email = capture_literal('email', ['Case', '+tag', '@', 'EXAMPLE.COM'])
assert email.original == 'Case+tag@EXAMPLE.COM'
assert email.canonical == 'Case+tag@example.com'
assert capture_literal('email', ['case+tag@example.com']).canonical != email.canonical
assert '+' in email.canonical
assert capture_literal('email', ['a' * 64 + '@example.com']).disposition == 'candidate'
assert capture_literal('email', ['a' * 65 + '@example.com']).disposition == 'unsupported'
assert capture_literal('email', ['a@' + 'x' * 63 + '.example']).disposition == 'candidate'
for literal in ('"quoted local"@example.com', 'δοκιμή@example.com',
'name@例え.example', 'a..b@example.com', '.a@example.com',
'a.@example.com', 'a@-bad.example', 'a@bad-.example',
'a@localhost', 'a@example.com.', 'a@@example.com',
'a b@example.com', 'a@' + 'x' * 64 + '.example'):
assert capture_literal('email', [literal]).disposition == 'unsupported'
for literal in ('-AB', 'A B', 'A/B', 'АB', 'A' * 33):
assert capture_literal('order-id', [literal]).disposition == 'unsupported'
for bad_groups in (None, True, 'AB', [], [''], [12], [False], ['x' * 255]):
rejects('order-id', bad_groups)
for bad_positions in ([True], ['0'], [-1], [2], [0, 0], None):
rejects('order-id', ['AB'], bad_positions)
rejects('phone', ['AB'])
try:
saved.canonical = 'ZZ-99'
except FrozenInstanceError:
pass
else:
raise AssertionError('mutable capture')
payload = {'kind': email.kind, 'candidate': email.canonical}
assert json.loads(json.dumps(payload)) == payload
print(json.dumps({'fixture_class': 'Synthetic', 'order_id': saved.canonical,
'email_original': email.original, 'email_candidate': email.canonical,
'disposition': email.disposition,
'approval': 'not-performed', 'delivery': 'not-tested',
'ownership': 'not-tested', 'account_lookup': 'not-performed'}))
The printed report emits the preserved order ID, original email surface, and candidate email spelling actually computed by the function. It labels the fixture Synthetic and leaves approval, delivery, ownership, and account lookup unperformed or untested. These are real limits of the executed artifact, not hidden success claims.
The function itself has no conversation state machine. A new call produces a new immutable candidate snapshot. A separate controller must decide which candidate is current, whether a repair replaced it, and whether a completed caller reply approves it before any downstream use.
Interpret the rejection and ownership fixtures
The mutable-input fixture changes the caller's original group list after capture. The saved result must retain its original tuple and literal. This protects the ordinary ownership guarantee an application needs when a renderer or adapter holds a previously returned candidate.
The frozen-result fixture then attempts to overwrite the canonical field. Ordinary dataclass assignment must fail. The artifact does not claim a sandbox against hostile Python reflection; it establishes a clear owned snapshot under normal API use, with no mutable list hidden inside its result.
The letter and punctuation fixtures preserve supported characters rather than normalizing them into one another. Lowercase and uppercase order IDs remain different under the teaching policy. Hyphens and underscores are supported literal characters, while spaces and slashes are outside the chosen grammar.
The pronunciation-dictionary versioning guide is useful when a separate renderer adds spoken forms for characters or product terms. Version that presentation policy without letting a pronunciation update rewrite previously captured identifier values.
The email fixtures distinguish local-part case and preserve plus tagging. They also reject consecutive local-part dots, unsupported quoted forms, malformed label edges, non-ASCII input, and an oversized label. Again, unsupported means outside this route; the code does not make a claim about every possible address accepted by all email systems.
The invalid-argument fixtures reject missing groups, numeric groups, empty groups, and malformed uncertainty metadata. Those are API errors rather than caller-approved empty values. A consuming application should route them visibly and inspect the producer instead of inventing a plausible identifier to keep the workflow moving.
Design a repairable spoken readback
Read back the literal in groups that make its characters distinguishable under the current language and destination contract. Include punctuation where it is material. Preserve an independent authoritative string while generating a spoken surface that the caller can correct.
The technical-abbreviation guide explains why the same visible sequence can be pronounced differently depending on context. That presentation choice should help a caller distinguish characters; it must not decide which characters were captured without evidence.
If a caller says a group is wrong, ask for that group or disputed position. Avoid a broad cleanup routine that repeatedly guesses familiar words from spelled letters. A targeted repair can preserve the parts already established while keeping the corrected candidate visibly separate from the old one.
The confirmation prosody guide describes the audible presentation boundary. A string equality assertion cannot establish speech intelligibility. Evaluate actual synthesized audio, turn-taking, and the caller's completed reply separately from the pure character fixture.
Use a spelling convention only when the caller understands it. An application can offer letter names or a phonetic clarification under an explicit script, but no convention guarantees that every caller will distinguish every sound. Preserve unresolved positions instead of converting a hesitant answer into a confident candidate.
The regional dataset guide is relevant when gathering real evaluation evidence. Use consented, appropriately retained material and separate cohorts or tasks by their actual purpose. Synthetic identifiers can test state transitions without being presented as observed customer recognition results.
Invalidate approval after material repairs
A corrected character produces a new material candidate. Approval of the old spelling does not approve the new one. Changing the field kind, destination normalization policy, or account context can also be material even if some visible characters remain unchanged.
The yes-but repair guide explains why a reply combining acknowledgment and correction must be processed as a repair. Matching an affirmative fragment before the caller finishes can confirm a spelling that the full reply explicitly rejects.
The workflow-enforcement guide describes how deterministic state can protect these boundaries. A controller should bind approval to the exact current candidate and material context. The pure capture helper cannot supply that guarantee simply by returning a nonempty canonical string.
If confirmation involves an await, recheck candidate identity, age, and ownership after it completes. A late approval result for an older spelling cannot authorize a corrected identifier. An expired or canceled attempt cannot revive because a recognizer or renderer eventually returned a plausible reply.
The DTMF and voice capture guide adds the modality boundary. A switch to keypad input transfers input ownership under the workflow's contract. It does not authenticate the caller, and a handoff to another route must not leave stale approval attached to the previous candidate.
This artifact deliberately implements none of that asynchronous controller machinery. Its small scope gives that controller an immutable literal and explicit disposition. If a controller adds request identities, new material confirmation requests need new identities, while exact retries must preserve their original frozen payload and timing evidence.

Separate syntax from downstream evidence
A supported address candidate is not evidence of a deliverable mailbox. A deliverable mailbox is not evidence that the caller owns it. An account lookup match is not automatically permission to disclose account information. Keep each claim tied to the evidence-producing operation that actually established it.
The same applies to order IDs. A string matching a supported pattern may be unassigned, stale, mistyped, or associated with another person. A format classifier should not return an authenticated flag or imply that the next adapter can skip its own authorization and account-resolution contract.
The memory and consent guide explains why retained facts need their own identity, consent, and expiry evidence. A previously captured contact address must not silently become a reusable ownership credential on a later call.
If a lookup returns several possible records, preserve that uncertainty. Do not choose the nearest spelling or reveal information from a likely match before the workflow has appropriate authority. This article performs no lookup, so it has no evidence about record uniqueness, authorization, or actual account availability.
The outcome metrics guide helps keep those stages visible in evaluation. Count supported candidates, clarification attempts, unsupported routes, caller rejections, and actual downstream outcomes under the real contract. A syntax acceptance rate is not an account-resolution or authentication success rate.
Test the actual transfer boundaries
RFC 8259 defines JSON strings and their representation. The reference tests that its candidate payload survives Python's JSON encode/decode round trip. That proves the executed boundary only; it does not certify a production SDK, database schema, or lookup service that was never invoked.
Inspect the actual receiving schema. An order identifier should not pass through numeric coercion. An email field should not be globally lowercased by a mapper that silently changes the local part. A display formatter should not replace literal punctuation with spoken words in the submitted payload.
The observability guide describes how to organize evidence across components. A useful investigation compares the captured literal, canonical candidate, current approval snapshot, submitted payload, and receiving interpretation. Preserve only material evidence under justified access and retention rules; this synthetic fixture needs no customer identifiers.
An exact transport retry must reuse its original request identity and frozen payload. A correction is a new semantic request. Mixing those cases can either duplicate an operation or cause a corrected identifier to be mistaken for a retry of a different value. The capture helper performs no remote operation, so adapter guarantees need their own complete fixtures.

Choose a capture acceptance matrix
Start with a small matrix that names supported preservation and explicit unsupported outcomes. This table describes the reference and surrounding handoff boundaries. It does not claim that one grammar can validate every issuing system or every possible email address.
| Criterion | Supported decision ✓ | Unsupported shortcut ✗ |
|---|---|---|
| Order ID | Preserve declared literal characters | Convert digits to an integer |
| Case | Keep destination-sensitive case | Lowercase every field globally |
| Email local part | Preserve spelling and plus signs | Apply one provider's alias rules everywhere |
| ASCII domain | Normalize case separately | Rewrite an unfamiliar domain by popularity |
| Uncertainty | Keep positions and clarify | Substitute a likely character |
| Unsupported email syntax | Offer another supported route | Claim global invalidity |
| Ownership | Owned immutable capture snapshot | Retain a mutable caller list |
| Approval | Current material candidate | Reuse approval after correction |
| Downstream evidence | Separate delivery and account checks | Treat syntax as authentication |
Choose length and character limits from the actual destination contract when adapting the example. The teaching order-ID grammar has no authority over a real commerce platform's identifier format. The ASCII email subset likewise requires a genuine alternate route before a product can claim to support addresses outside it.
Test negative cases that could otherwise look plausible: a missing character replaced by a familiar letter, a significant leading zero removed, a plus tag stripped, a local part lowercased, and a new correction sharing an old approval. These failures are especially important because the resulting string may still look syntactically tidy.
Keep repair ownership explicit
A character-level repair is useful only when it belongs to the right candidate. Suppose a caller starts correcting the last group while an earlier readback is still playing. The capture helper can preserve both strings accurately, but it cannot decide which recognition result now owns the active input. That decision belongs to the surrounding controller and must be explicit.
The interruption-safety guide explains why late completion cannot reclaim a newer generation's ownership. The same principle applies to spelling. A response associated with an older candidate must not overwrite a corrected address simply because it arrives later or contains a syntactically cleaner string.
Keep material candidate identity separate from a display label. A label such as email-address describes the kind of field; it is not an identity for every candidate ever captured in that field. Similarly, a missing typed candidate should remain missing rather than being replaced by a human-readable status word that another component might accidentally submit as literal data.
The reference uses None for an absent canonical candidate. Its disposition explains why there is no candidate, while the original surface and uncertainty positions remain available for authorized repair. That separation prevents the words clarify or unsupported from becoming fake email addresses or order identifiers in a generic string pipeline.
A fresh correction should be parsed as a fresh immutable snapshot. The application may preserve unchanged groups under a declared repair contract, but it must still validate the complete resulting literal. Fixing one character can expose a different unsupported boundary, such as an oversized field or malformed punctuation. Do not approve only the repaired fragment while silently trusting an earlier partially checked remainder.
When an attempt expires or transfers to another route, its approval evidence must not survive by accident. The pure function has no terminal states and therefore makes no expiry guarantee. A controller introducing those states must check expiry on policy queries as well as callbacks, process corrections only for the active attempt, and refuse late work that belongs to a completed or canceled owner.
These mechanisms should remain small and inspectable. A literal parser does not need to become a complete conversation engine to support them. It needs to return explicit evidence that a controller can own: the field kind, immutable source groups, original literal, uncertainty positions, candidate spelling when present, and a named disposition.
Review the literal a reader actually receives
The most useful artifact test executes the full published fence, not a nearby implementation that happens to share its function name. A missing assertion, changed regex, or copied fixture can otherwise leave the article advertising behavior that the delivered code does not implement. Keep the expected values in the same runnable artifact as the function being taught.
The additional boundary assertions illustrate this discipline. They test the maximum supported group count and the next unsupported count, a local part at its declared limit and just beyond it, and a domain label at its declared limit. These are synthetic representation tests. They are not a catalog of globally deliverable email addresses or evidence that an issuing system recognizes the order ID.
The uncertainty-alias fixture is equally important. It changes the caller's position list after capture and verifies that the saved tuple remains unchanged. Without that check, a result could look frozen while a nested mutable object silently changed which character needed repair. The groups fixture protects the corresponding source-literal boundary.
Inspect both original and canonical email surfaces in the emitted report. The original retains the domain's supplied case; the candidate lowercases only that ASCII domain while preserving the local part and plus sign. A global lowercase operation would therefore fail the fixture even though its output still looked like an ordinary address to a human reviewer.
Do not extend the supported subset by deleting whatever prevents a match. Removing spaces, quotes, punctuation, or non-ASCII characters can turn an unsupported address into a different address. If the interpretation layer maps spoken punctuation words into characters, it needs its own explicit evidence and confirmation. The reference receives literal groups after that interpretation has already occurred.
The same review discipline applies to documentation examples. A reserved domain keeps personal contact information out of the fixture, while an invented order ID demonstrates leading zero preservation without claiming to identify a real purchase. Keep those examples visibly synthetic and avoid attaching fabricated recognition rates, account lookup results, or customer quotations.
Finally, compare the adapter's actual submitted payload with the current approved literal. A successful parser assertion establishes only this local boundary. A database mapper, SDK serializer, or account service can still apply its own transformation later. Investigating the exact boundary that changed the value is more useful than repeatedly changing the recognizer when the source characters were already correct.
The disposition also provides a concrete routing boundary for a user interface. Candidate means the supported literal is available for the next confirmation step. Clarify means unresolved positions still prevent that handoff. Unsupported means this route lacks the required representation capability. An invalid API argument is an engineering failure that should be inspected separately. Keeping those meanings distinct makes recovery more predictable than attaching one generic invalid label to every case.
None of these outcomes should cause automatic account disclosure. The function does not know who supplied the string, whether the associated account exists, or what the caller is allowed to access. A consuming workflow can use a confirmed identifier to request a separately authorized lookup, but it must retain the lookup's actual result and follow its own access rules. Syntax classification alone provides none of that authority.
This is why the emitted report includes explicit untested and unperformed fields. They make the artifact's evidence boundary inspectable at the same place as its successful character assertions. An engineer running the fence receives the actual preserved strings and a clear statement of what the run did not establish. That is enough to verify the small capture contract while leaving broader deployment claims to real conversation and adapter evidence.
Frequently asked questions
Why are order IDs literal strings?
Their meaning comes from the issuing system's identifier contract, not arithmetic. Leading zeroes, case, and punctuation can distinguish records. Preserve supported characters exactly and use the destination's explicit rules rather than converting a numeric-looking identifier into a number.
How should a caller spell an email address?
Use a script that makes groups and punctuation clear and permits targeted correction. Evaluate the actual spoken presentation and caller response. No single spelling convention guarantees correct capture; uncertainty should remain explicit until the material characters are resolved.
Should an email local-part be lowercased?
Do not apply that globally. Preserve its spelling under the destination's semantics. The reference deliberately retains local-part case and tests that two differently cased local parts remain distinct candidates under its capture policy.
Can the domain be normalized separately?
Yes, this ASCII teaching subset lowercases the domain while retaining the full original surface. That operation is separate from local-part interpretation. Supporting internationalized domains requires an additional declared contract rather than an implicit conversion hidden in this helper.
Are plus signs always removable?
No. Keep them as literal local-part characters unless a specific destination explicitly authorizes another interpretation. Provider-specific alias behavior is not a universal email rule, and a capture function should not erase a plus tag to guess an account key.
How do I distinguish O from zero?
Ask a targeted question about the disputed position or group. Keep the alternatives unresolved until the caller clarifies. A plausible format or a familiar account pattern does not authorize substituting a letter for a digit or vice versa.
When should grouping be used?
Use it when it makes readback and repair manageable for the current field and caller. The reference retains groups but inserts no separators. A pause or display grouping must not become an extra literal character unless that character was actually supplied under the capture contract.
What does a repair change?
It replaces the material candidate or its context. A corrected character, field kind, or destination policy can require fresh validation and approval. Preserve the old snapshot as evidence where appropriate rather than mutating it into a value that was never actually presented.
How does an old confirmation become stale?
It belongs to the candidate and context that were current when requested. A later material repair invalidates it. A separate controller must enforce revision, age, and ownership after asynchronous work; the pure character helper returns no approval token.
Can a regex prove deliverability?
No. It can enforce the declared syntax subset only. Mailbox existence, transport capability, delivery outcome, and caller ownership require different evidence. The published reference sends no message and reports those downstream claims as untested.
Does valid syntax establish account ownership?
No. A well-formed string can identify someone else's mailbox or order. Keep account lookup and authorization separate from literal capture. The reference deliberately returns a candidate disposition without an authenticated or account-owner flag.
What email subset does the example accept?
It accepts a bounded unquoted ASCII dot-atom local part and an ASCII domain with supported label shapes and at least two labels. It is a teaching subset, not a complete RFC validator. The code and negative fixtures make its limitations explicit.
Are internationalized addresses invalid?
No. They are unsupported by this particular ASCII route. SMTPUTF8 and other relevant standards define broader capabilities. A product supporting them needs an explicit compatible parser and transport route rather than relabeling a subset rejection as global invalidity.
How do I handle unsupported quoted local-parts?
Preserve the original and offer an actually supported alternate route. Do not remove quotation marks and assume the result has the same meaning. The reference returns unsupported because it does not implement that syntax, not because every quoted address is invalid.
Should hyphens and underscores be interchangeable?
Only if the destination explicitly defines that equivalence. The order-ID reference preserves both as distinct literal characters. A pronunciation convenience or a visual resemblance cannot authorize replacing one with the other in a lookup payload.
Can Unicode normalization change an identifier?
It can change the representation under defined equivalence rules, and compatibility transformations can erase distinctions a destination cares about. Use only transformations authorized by that field's contract. This reference performs no Unicode normalization and retains unsupported source text.
How do synthetic example domains avoid personal data?
Use reserved documentation domains such as example.com and invented local parts. They demonstrate spelling without borrowing a real person's contact details. The fixture does not contact that domain, send mail, or imply that a mailbox exists.
What should uncertain character positions contain?
Use explicitly defined indices in the captured literal under your indexing contract. This reference accepts unique integer positions within the joined string and rejects malformed metadata. It does not hide uncertainty inside a guessed replacement character or a misleading empty candidate.
How do immutable approval snapshots prevent mutation?
They keep the material value associated with an approval stable under ordinary use. The reference supplies immutable capture snapshots for a separate controller; it does not implement approval itself. That controller must bind its own evidence to the exact current snapshot.
What happens after the caller rejects the repeat-back?
The surrounding workflow should return to repair or another supported route and invalidate approval of the rejected spelling. It must not submit the previous candidate merely because it passed syntax checks. The pure helper can produce a fresh snapshot after the corrected literal is supplied.
Ship an inspectable character boundary
Preserve the supplied characters, expose unresolved positions, and keep destination-specific normalization separate from speech presentation. A supported candidate should say exactly what it proves and leave delivery, ownership, account matching, and authorization to their actual evidence sources.
For 60db Agent integrations, the complete synthetic fixture provides a small first boundary that another engineer can execute and inspect. Use it to protect literal capture, then verify the real confirmation and adapter contracts before claiming that an identifier resolved the caller's intended account or task.
