60db Logo
  • Engineering

Version pronunciation dictionaries like application code

Bind pronunciation entries, target voice, locale, context and review evidence to an immutable release digest with safe rollback.

Editorial date
Reading time
26 min
Two retained dictionary snapshots connect to a separate review and selection marker

TL;DR: Release a pronunciation dictionary as immutable content scoped to a provider, voice, locale and context. Bind review evidence to that exact digest, reject changed content under an existing version, and make rollback select a retained reviewed snapshot rather than edit history.


A pronunciation fix improves one label and changes another that no one meant to touch. The dictionary filename is unchanged, cached audio still refers to the old contents, and the review record names only a version string. The team cannot determine which rule produced the sample a caller heard.

Start with a frozen release value. Include target synthesis conditions and exact reviewed entries in a deterministic, schema-specific serialization, then compute its digest. A review that approves another digest is not approval of the current release.

The Pronunciation Lexicon Specification defines representations for graphemes, aliases and phonemes. It does not supply your application's release identity or activation authority. The regression testing method provides the surrounding discipline for preserving fixtures across changes.

The standard-library artifact below validates a narrow alias-only release, records immutable versions and emits an inspectable diff. It is not a deployment system, external lexicon fetcher or acoustic benchmark for 60db Agent. Its passing tests establish metadata and content behavior, while listening review and actual activation remain explicit external responsibilities.

Table of contents

  1. Define the released value
  2. Make scope part of pronunciation identity
  3. Parse and serialize one explicit schema
  4. Bind review to exact content
  5. Validate and diff an immutable snapshot
  6. Keep activation and rollback explicit
  7. Test text behavior and listening separately
  8. Diagnose a dictionary regression
  9. Frequently asked questions
  10. Release a small reviewed change

Define the released value

A dictionary release is more than an entry file. It includes the schema, normalization policy, target synthesis scope, immutable entry set and evidence that the relevant review occurred. Decide which values determine behavior before assigning a release version.

Keep authored content separate from a review record. The content digest identifies what was proposed. The review record names that digest and its scope. This avoids a circular hash in which adding an approval changes the content it claims to approve.

A version is a human-meaningful immutable reference. Once it names a released snapshot, presenting different material values under that version should be a conflict. A different version may contain identical entries, but its identity remains separately recorded if the schema includes it in the release value.

The example includes the version in its digest intentionally. That means two release versions with identical aliases have different release digests. State this rule so readers do not expect a version-independent content-addressing scheme that the program does not implement.

Do not retain a mutable authoring mapping as the released value. Copy supported leaves and collections into owned immutable structures. A later editor changing an alias list must create a new proposal rather than changing what an earlier reviewer approved.

Call settings isolation has the same ownership lesson. Sharing a reference can make one caller's update affect another operation. A dictionary publisher should provide a snapshot, not a live mutable authoring object whose contents change during synthesis.

Keep old snapshots available under a declared retention policy. They explain historical output and provide a rollback target. Removing a notification from a work queue should not erase the released content required to reproduce the decision.

An initial implementation can remain small. One exact alias schema, one target scope, a strict loader and a release ledger are enough to demonstrate the contract. Adding a generic deployment control plane would introduce responsibilities that a teaching artifact cannot honestly complete.

A grapheme, locale and context form a scoped rule key before an approved rendering is attached

Define the release boundary in the authoring workflow before content is serialized. A draft can change while reviewers discuss it. Once a snapshot is proposed for review, own its supported values and give subsequent edits another proposal identity. That prevents a reviewer from approving one screen while a publisher later reads a changed mapping.

The content digest should identify every material field needed to reproduce selection. A digest over entries alone would miss a changed voice or locale. A digest over a filename would miss changed contents. The fixed serialization should make those choices visible rather than relying on an undocumented helper's defaults.

Separate release identity from attempt identity. Uploading or selecting the same reviewed snapshot twice can be two operational attempts without two different dictionary contents. Preserve the snapshot digest in both attempts and keep their transport or activation receipts separately owned.

The example deliberately stops before operational activation. It gives a reader a complete value-level contract they can execute and inspect. A production system can attach that contract to an existing release process without introducing another service solely to store a small dictionary.

Make scope part of pronunciation identity

A rule that is useful in one context can be wrong in another. A token may be a brand label, abbreviation, person name or literal identifier. Include the trusted context in selection rather than making the token alone choose its spoken form.

Technical abbreviation handling narrows that selection problem with bound spans and explicit context. A lexicon is not a semantic classifier, and an alias matching a spelling does not prove that the current occurrence belongs to its intended domain.

The target provider and voice belong in the release scope when review depends on their synthesis behavior. Locale and context do too. A review for one voice must not become approval of another merely because both accept the same SSML syntax.

Amazon Polly's lexicon documentation describes regional storage and use of specified lexicons during synthesis. Preserve relevant region and endpoint configuration in a real adapter. The example's generic provider field is a synthetic scope value, not an implementation of that provider's API.

Do not infer common support across providers from a standards-compliant file. Google's SSML documentation documents its own supported controls. A successful local parse cannot establish voice compatibility, lexical precedence or pronunciation quality.

The example uses aliases only. It rejects extra fields, including a proposed phoneme mode, rather than pretending to validate an arbitrary phonetic alphabet. A future phoneme schema needs approved alphabet identifiers, target support, entry review and negative fixtures of its own.

The brand and URL rendering contract keeps presentation separate from the canonical value. The dictionary should supply a reviewed projection, not mutate a stored URL, product identifier or customer record to make it easier to speak.

For names, use an appropriate person or session boundary. The surname clarification method separates preferred pronunciation from identity and retention permission. A global dictionary entry is not an automatic destination for every caller-provided name correction.

Caches should include the snapshot identity where it affects sound. The cache-key guide explains why policy and language can affect reuse. Keep actual target settings in the reproduction packet instead of relying on a cache label that no longer resolves to the original values.

Record provider support as evidence for the target, not as part of a vague approved-everywhere flag. SSML 1.1 defines standard controls, while providers can expose different supported interfaces. A release review must identify which actual contract its fixtures and recordings exercised.

Keep data escaping in the synthesis adapter separate from release approval. A reviewed alias can contain an ampersand or text that resembles markup. It remains data. ElementTree's construction API can serialize text inside trusted elements, but dictionary review does not authorize arbitrary caller-selected tags or external resources.

If a deployment imports an external lexicon, the adapter must own the retrieval policy and retained content identity. A URI can refer to changing contents. Do not assume that a release digest over the URI string identifies the bytes a provider eventually retrieved, and do not fetch an untrusted URI merely because it appears in a dictionary record.

The offline schema avoids that problem by carrying admitted alias values directly. Its target fields are synthetic control values supplied by a trusted configuration owner. The program does not discover provider capabilities, fetch phonetic resources or infer which regional endpoint a deployment uses.

Parse and serialize one explicit schema

JSON is an interchange syntax, not a pronunciation schema. RFC 8259 describes its data model and interoperability considerations. A release validator must still decide which fields exist, their types and how duplicate object names are handled.

Reject duplicate keys while parsing. A later schema check cannot see an overwritten value if the decoder has already kept only one occurrence. The reference loader uses an object-pairs hook to make that conflict observable before values become an ordinary dictionary.

Reject nonfinite constants too. A parser's permissive handling of values such as NaN is not a reason to admit them into a release. This schema contains strings and entry lists, so numbers have no valid role in the authored release at all.

Keep type checks exact where Python's type relationships can surprise you. A boolean should not become a revision number in a schema that expects a positive integer. The current schema uses version strings; a future numeric revision design needs its own strict admission check.

The serializer in this article is deliberately schema-specific. It builds one fixed structure, sorts the unordered entry set, and uses explicit JSON separators and encoding. It is not a general implementation of a universal JSON canonicalization standard.

Do not sort every list reflexively. Some schemas give order semantic meaning, such as rule precedence or candidate-language order. The entry set here is unordered because duplicate graphemes are forbidden within one release scope and no precedence rule exists.

The reference keeps raw grapheme strings and aliases exact. It performs no silent normalization or case folding. Unicode normalization describes a different text-equivalence contract; adopting it for keys would require a declared policy and collision fixtures.

Mixed-script preparation preserves original text while deriving a projection under an explicit field policy. That is preferable to normalizing an entire release invisibly and then claiming the digest identifies the exact strings the author submitted.

A useful serialization test changes only JSON whitespace, object-key order and entry order. The admitted snapshot and digest should remain equal under this declared schema. Changing the target voice or an alias should produce different released content, even if a reviewer thinks it would sound similar.

Test parsing before testing hashing. Duplicate object keys can conceal authoring mistakes even when the final loaded mapping has a valid shape. Rejection at the decoder boundary keeps the original conflict observable instead of silently selecting a value according to an implementation's duplicate-name behavior.

Then test ownership before publishing a digest. The harness changes the original nested entry after admission and verifies that the snapshot remains unchanged. This protects a review from later alias mutation without requiring the caller to remember that a shared list has become effectively read-only.

Keep byte encoding explicit. This example serializes valid Unicode scalar strings as UTF-8 and rejects isolated surrogate code points. Another encoding or a new schema would be another digest contract. Record that change rather than claiming that a hash by itself explains how the underlying bytes were chosen.

Do not conflate source formatting with content changes. Whitespace around JSON punctuation and object-key order are immaterial here, while whitespace inside an alias is part of its exact value. Trimming every string before hashing would silently change the authored speech rule and could conceal a meaningful review difference.

An empty entry set can be a valid explicit snapshot under this schema. It means no aliases are supplied for that target, not that every token is approved or that missing review can be ignored. Keep the empty content and its review binding inspectable when such a release is intentionally selected.

Bind review to exact content

A review record should identify the release digest and the scope reviewed, plus an evidence reference owned by the review process. Its existence in a dictionary is not proof that a real person listened. The validator can check the binding supplied by a trusted adapter, not certify how the evidence was obtained.

Keep per-item annotations separate from stable release settings. Different entries can have independent review records without changing the provider or voice identity. The paired language-policy ledger shows why an item's audit revision should not be mistaken for a different stable policy.

Bind the review to the authored snapshot after schema validation. A review attached only to a filename or version label can approve content that was later overwritten. Exact digest matching makes that mismatch detectable, while immutable version publication prevents the overwrite from becoming accepted history.

An approval must also match the target scope. A digest for one locale cannot justify a different locale merely because an alias string is identical. Preserve provider, voice, locale and context in the review packet so a reviewer can recompute the relationship.

Separate text fixture approval from listening evidence. A passing duplicate-key test proves parser behavior. A listening record concerns actual captured audio under the declared conditions. The release process may require both, but one cannot impersonate the other.

Use the following criteria table to keep those conclusions separate. A check means sufficient for that narrow metadata property under trusted evidence inputs; a cross means another proof is required. It is not a vendor or speech-quality ranking.

Evidence Immutable release identity Exact review binding Acoustic quality established
Version label alone ✗ Content may differ ✗ No digest binding ✗ No audio evidence
Frozen snapshot and deterministic digest ✓ Within declared schema ✗ Review still missing ✗ No audio evidence
Trusted review matching digest and scope ✓ Snapshot still required ✓ Exact supplied binding ✗ Metadata cannot prove listening
Qualified review of captured target audio ✗ Keep release record too ✓ If linked to exact release ✓ Only within actual review boundary

Confirmation prosody supplies a useful review question: are material alternatives easy to distinguish? A preference for one pronunciation does not automatically establish correct interpretation or business completion.

Do not fabricate listening scores to make a release gate look complete. Mark audio review pending until it exists. If the current task is to publish a measurement method, say exactly which artifact properties have been tested and which acoustic properties remain unmeasured.

Schema checks, regression fixtures and human review precede a release digest's activation decision

A review packet should state what was actually examined. Source spelling review, alias selection tests, provider compatibility checks and listening observations are related activities with different outputs. Keep them linked to the same proposed snapshot without replacing all of them with an unexplained approved status.

If a reviewer finds an ambiguous entry, create a new draft rather than editing the immutable snapshot beneath the existing record. The changed alias needs another digest and another binding. Historical evidence remains useful because it explains which content the reviewer rejected or requested to revise.

Allow reviewers to record uncertainty. A text fixture can pass while listeners disagree about whether two material labels are distinguishable. Preserve that disagreement and the listening instructions rather than forcing a numeric quality score to make the release gate look complete.

For personal pronunciation preferences, review and retention permission are separate. A caller may provide a useful session-specific form without authorizing its storage in a durable dictionary. Keep any durable person-scoped rule attached to the owning permission process, not just to a general release-review token.

Validate and diff an immutable snapshot

The reference loader accepts a fixed root schema and alias entries with exact fields. It returns a frozen snapshot, computes the declared release digest, checks a trusted review mapping and records one immutable value per version. It also emits added, changed and removed entry keys for inspection.

The target scope is a single provider, voice, locale and context for each snapshot. That narrow choice avoids inventing a precedence engine across several scopes. A real adapter can select the appropriate snapshot before synthesis under its own authorized policy.

Save the complete fence as dictionary_release.py and run it with Python 3.12 or newer. Every provider, voice and evidence value is synthetic. The printed diff is an actual output of the example; no audio, external file retrieval or production activation occurs.

from dataclasses import dataclass, FrozenInstanceError
from hashlib import sha256
import json

def text(value):
    if type(value) is not str or not value or any(0xD800 <= ord(c) <= 0xDFFF for c in value):
        raise ValueError("nonempty Unicode scalar text required")
    return value

def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("duplicate JSON object key")
        result[key] = value
    return result

def nonfinite(value):
    raise ValueError("nonfinite JSON value")

@dataclass(frozen=True)
class Snapshot:
    version: str
    target: tuple
    entries: tuple

    @property
    def digest(self):
        payload = json.dumps(("alias-schema-A", self.version, self.target, self.entries),
                             ensure_ascii=False, separators=(",", ":"))
        return sha256(payload.encode("utf-8")).hexdigest()

def load(raw):
    if type(raw) is not str:
        raise ValueError("JSON text required")
    data = json.loads(raw, object_pairs_hook=unique_object, parse_constant=nonfinite)
    required = {"version", "provider", "voice", "locale", "context", "entries"}
    if type(data) is not dict or set(data) != required:
        raise ValueError("unsupported release schema")
    target = tuple(text(data[k]) for k in ("provider", "voice", "locale", "context"))
    if type(data["entries"]) is not list:
        raise ValueError("entry list required")
    owned, keys = [], set()
    for row in data["entries"]:
        if type(row) is not dict or set(row) != {"grapheme", "alias"}:
            raise ValueError("alias-only entry schema required")
        key, alias = text(row["grapheme"]), text(row["alias"])
        if key in keys:
            raise ValueError("duplicate scoped grapheme")
        keys.add(key)
        owned.append((key, alias))
    return Snapshot(text(data["version"]), target, tuple(sorted(owned)))

def reviewed(snapshot, approval):
    return (type(approval) is dict and set(approval) == {"digest", "target", "evidence"}
            and approval["digest"] == snapshot.digest
            and type(approval["target"]) in (list, tuple)
            and tuple(approval["target"]) == snapshot.target
            and type(approval["evidence"]) is str and bool(approval["evidence"]))

@dataclass(frozen=True)
class Approval:
    digest: str
    target: tuple
    evidence: str

@dataclass(frozen=True)
class Release:
    snapshot: Snapshot
    approval: Approval

class Releases:
    def __init__(self):
        self._versions = {}

    def record(self, snapshot, approval):
        old = self._versions.get(snapshot.version)
        if old is not None and old.snapshot != snapshot:
            return "version_conflict"
        if not reviewed(snapshot, approval):
            return "review_missing_or_mismatched"
        if old is None:
            owned = Approval(snapshot.digest, snapshot.target, approval["evidence"])
            self._versions[snapshot.version] = Release(snapshot, owned)
        return "recorded" if old is None else "duplicate"

    def get(self, version):
        return self._versions[version].snapshot

    def review(self, version):
        return self._versions[version].approval

def diff(before, after):
    if before.target != after.target:
        raise ValueError("compare one target scope")
    old, new = dict(before.entries), dict(after.entries)
    return dict(added=tuple(sorted(new.keys() - old.keys())),
                removed=tuple(sorted(old.keys() - new.keys())),
                changed=tuple(sorted(k for k in old.keys() & new.keys() if old[k] != new[k])))

def synthetic(version="release-A", **changes):
    data = dict(version=version, provider="synthetic-provider", voice="synthetic-voice",
                locale="en-US", context="catalog-A",
                entries=[{"grapheme": "SyntheticBrand", "alias": "synthetic label"}])
    data.update(changes)
    return data

def approve(s):
    return dict(digest=s.digest, target=list(s.target), evidence="synthetic-review-record")

raw = synthetic()
a = load(json.dumps(raw))
raw["entries"][0]["alias"] = "mutated"
assert a.entries == (("SyntheticBrand", "synthetic label"),)
assert load(json.dumps(synthetic(), sort_keys=True, indent=4)) == a
unordered = synthetic(entries=[{"grapheme": "z", "alias": "zee"}, {"grapheme": "a", "alias": "ay"}])
x = load(json.dumps(unordered))
unordered["entries"].reverse()
assert load(json.dumps(unordered)).digest == x.digest
ledger = Releases()
first_approval = approve(a)
assert ledger.record(a, first_approval) == "recorded"
duplicate_approval = {**approve(a), "evidence": "later-duplicate-review"}
assert ledger.record(a, duplicate_approval) == "duplicate"
assert ledger.review("release-A") == Approval(a.digest, a.target, "synthetic-review-record")
changed = load(json.dumps(synthetic(voice="another-voice")))
assert changed.digest != a.digest
assert ledger.record(changed, approve(changed)) == "version_conflict"
b = load(json.dumps(synthetic("release-B", entries=[{"grapheme": "Other", "alias": "other label"}])))
assert ledger.record(b, approve(a)) == "review_missing_or_mismatched"
assert ledger.record(b, approve(b)) == "recorded"
assert ledger.get("release-A") == a  # retained rollback reference, no activation performed
assert diff(a, b) == {"added": ("Other",), "removed": ("SyntheticBrand",), "changed": ()}
for bad in ('{"version":"A","version":"B"}', '{"version":NaN}',
            json.dumps(synthetic(entries=[{"grapheme": "x", "alias": "a"}] * 2)),
            json.dumps(synthetic(entries=[{"grapheme": "x", "phoneme": "x"}]))):
    try: load(bad)
    except ValueError: pass
    else: raise AssertionError("invalid release accepted")
try: a.entries = ()
except FrozenInstanceError: pass
else: raise AssertionError("snapshot rebinding accepted")
assert not reviewed(a, {**approve(a), "target": ["wrong-scope"]})
assert not reviewed(a, {**approve(a), "evidence": ""})
print("Synthetic release diff:", diff(a, b))
print("Synthetic digest:", a.digest)
print("Synthetic retained review:", ledger.review("release-A"))
print("Release fixtures passed; no audio review or activation performed.")

The admitted snapshot owns tuple entries and scalar strings. JSON formatting changes do not affect this schema's admitted value. A changed target voice changes the digest and conflicts with an existing version, even if every alias is unchanged.

The review function validates supplied binding only. Its evidence string is a trusted reference to an external process, not a signature, identity credential or proof that someone listened. A production review adapter needs its own authenticity and access boundary.

The ledger records reviewed snapshots and exposes retained values plus their original immutable approval references for historical audit. Existing get callers still receive a Snapshot, while review exposes the first accepted digest, target and evidence. An exact duplicate cannot overwrite that original evidence. The ledger has no active-version pointer and does not promote or roll back production. That limited output avoids labeling a passing offline fixture as a completed deployment.

Keep activation and rollback explicit

Activation is an owning application's decision to use one reviewed snapshot for new work. Preserve the selected version and digest with each synthesis request. An alias file being present in storage does not establish that an endpoint applied it.

A historical Azure lexicon question reports an abbreviation receiving an unexpected default expansion despite a custom lexicon. It is a real configuration symptom, not proof of a current service defect or universal precedence rule.

The activation adapter must verify the exact target interface, support and selected resource. External lexicon URLs are not implicit permission to fetch arbitrary caller resources. The offline example imports neither a network client nor an external XML parser.

Rollback should select a retained reviewed snapshot. Do not edit the current file to resemble an old version while keeping its new identity. Retained content and its original evidence let a reviewer explain what changed and what was restored.

A revoked or unsuitable snapshot can remain historical evidence without being selectable. A production release policy needs current selection eligibility, including any relevant expiry or revocation. The example's get method is a historical lookup, not an authorization decision.

An asynchronous activation adapter must check current selection authority after waiting. A newer decision, cancellation or deadline can make an earlier result stale. Shutdown semantics distinguishes terminal operation state from preserved historical results.

If a task repairs its material value, approval of an earlier spoken candidate may no longer apply. Self-correction handling keeps the new proposal distinct. Selecting a dictionary snapshot should not revive an expired operation identity or bypass the task's new confirmation requirement.

A qualified affirmative can change the value being confirmed. Preserve which text, alias and release were presented. An exact retry retains its original request identity; a fresh confirmation after repair needs another identity and immutable payload.

Changed dictionary content requires a new version rather than changing a released version and digest pair

A rollout should retain enough evidence to identify what new work selected. Record snapshot version, digest, target and the actual adapter request association. A storage update can succeed while synthesis still selects a previous resource, so a write acknowledgment alone cannot establish the sound used in an interaction.

If a provider caches external content, preserve the documented resource-selection and refresh behavior in the adapter evidence. Do not claim a universal cache duration from a historical forum thread. The offline release digest identifies the local snapshot and makes no statement about a remote cache's current contents.

Rollback is another selection decision, not deletion. A retained version can be referenced for diagnosis even when current policy no longer permits selecting it for new requests. The owning release process should distinguish historical lookup from an authorized activation path.

When an activation attempt is interrupted, keep its identity and disposition visible. Completed, canceled and not-attempted work are different operational observations. The small example does not run those operations; connecting it to an existing deployment workflow should preserve that workflow's durable accounting rather than treating a missing callback as success.

Test text behavior and listening separately

Text tests should cover schema rejection, duplicate keys, conflicting versions, scope mismatch, missing review and deterministic serialization. Preserve a failing fixture for every corrected defect. A validator that rejects every release is not correct merely because it rejects a malicious one.

A diff should report added, removed and changed keys under the declared scope. Emit that report when the article promises it. A test-success message alone does not give another engineer the reusable release evidence needed to inspect a change.

For listening review, capture actual audio generated from the exact snapshot under the target configuration. Keep source text, request payload, voice settings and audio digest. Short and long reply consistency is a separate review dimension that may matter when an alias appears in a larger explanation.

The speech-rate timing contract is another independent condition. If a review changes rate and dictionary content together, the result cannot isolate the dictionary change. Record both as distinct configuration inputs.

Audio capture must identify format, sample clock, channels, codec, decoder and capture point. Boundary defects can remove a material sound even when the dictionary selection was correct. Do not diagnose every unclear sample as a lexicon problem.

Consented dataset practices apply when real speakers or listeners contribute material. A reviewed pronunciation preference is not automatic permission to publish recordings, train a voice model or create a durable person-specific entry.

Keep missing evidence explicit. A release may pass schema and text fixtures while listening remains pending. That is a useful intermediate state; it becomes misleading only when the report calls it acoustic validation or activation-ready without the required external evidence.

No listening study is reported here. The code's synthetic review token exists to exercise binding behavior. It cannot supply pronunciation scores or establish that any real brand, abbreviation or surname sounds correct to callers.

Rollback changes selection to a retained reviewed version while preserving release history

Use matched source text when comparing old and new audio. A changed sentence can alter the review task even when it contains the same target token. Preserve the surrounding phrase and the selected rule so reviewers can distinguish a dictionary effect from changed context.

Keep one negative case where the token must remain literal, such as an identifier or a different context. A rule that improves the intended occurrence but expands the same substring inside a URL has introduced a regression. The selection layer must protect those fields before a dictionary can affect their presentation.

An output contract should also be tested directly. If an example promises a diff, assert its added, changed and removed fields and print a reusable representation. The reference does this for a synthetic release pair, rather than asking a reader to infer the report from a final success message.

Preserve the exact test runtime and source snapshot in the reproduction packet. A changed loader or schema can alter admission without a dictionary edit. Those are implementation changes that deserve their own regression evidence instead of being described as unexplained pronunciation drift.

Diagnose a dictionary regression

Begin with the release selected for the disputed synthesis request. Resolve its version to retained content and verify the digest. If the old content cannot be recovered, the investigation lacks a reproducible reference before any acoustic explanation is attempted.

Compare target settings next. A voice, locale, context or provider change can alter output without an entry edit. Preserve those differences in the report so a dictionary change is not credited or blamed for another configuration change.

Inspect the actual selected entry. A context mismatch can correctly produce literal fallback, while a duplicate or wrong key can indicate an authoring error. Keep literal identifiers protected; postal-code preservation shows why a speech-friendly transform must not become a global normalization rule.

If serialization differs, inspect the schema and parser. Duplicate object keys, nonfinite values and silent field dropping can make the loaded value differ from the apparent source. Keep rejected raw synthetic input in the regression fixture so the defect remains visible after a loader refactor.

If release and request are correct, inspect captured audio and delivery separately. Playback acknowledgments establish endpoint observations, not pronunciation preference or comprehension. An incomplete capture may have a different owner from the dictionary publisher.

Finally, compare the independent task outcome. A correctly spoken label can still refer to the wrong entity. The outcome-metrics boundary prevents a pronunciation pass from being presented as proof that the requested business action completed.

Text diff and fixture evidence remain separate from a target-voice listening decision

A useful handoff lets another engineer recompute a release without the author's working directory. Include the exact authored JSON, admitted schema version, target tuple, implementation/runtime version, resulting digest, review reference and emitted diff. Retain authorized source artifacts instead of a screenshot that cannot establish the original bytes or scope.

Perform the same reconstruction for a rejected change. A same-version conflict should show which material values differed; a review mismatch should show that the current digest or target was not the approved one. Keep sensitive reviewer records inside their actual access boundary and use synthetic evidence references in public reproductions.

When the loader itself changes, rerun the old fixtures before attributing a new result to dictionary content. A stricter schema may intentionally reject a previously admitted form, but that is a contract change requiring a clear migration decision. Do not silently transform the source and reuse the old review as if the release value had remained identical.

The next release can then be small and inspectable: one changed entry, a preserved literal negative case, a deterministic diff and the target audio review still required by the owning process. That gives the change a concrete purpose without predicting an unmeasured pronunciation gain.


Frequently asked questions

Why should pronunciation dictionaries be versioned?

They change application behavior and need a reproducible history. Bind each version to immutable entries and target scope, then retain the content used for historical synthesis. A filename or mutable storage object cannot explain which rule produced a disputed recording after its contents have changed.

What belongs in a dictionary entry?

The exact grapheme, declared scope and approved rendering belong in the application schema, with review evidence linked to the release. This example supports aliases only. Phoneme alphabets, precedence and broader provider features require explicit additional contracts instead of being assumed from a generic dictionary label.

Should locale and context be part of the key?

Yes when they affect selection or review. A token alone can refer to different things across tasks and language settings. The example scopes an entire snapshot to one provider, voice, locale and context, then forbids duplicate graphemes within that release.

How do aliases differ from phonemes?

An alias supplies another text rendering, while a phoneme representation uses an identified pronunciation alphabet. Support and interpretation depend on the synthesis interface. The alias-only artifact rejects phoneme entries rather than claiming that arbitrary phonetic strings are validated or universally supported.

Who approves pronunciation changes?

Use a declared review owner with appropriate source and actual listening evidence where required. Bind the review to the exact release digest and target. The validator checks supplied metadata; it cannot prove that the external review happened or that the evidence reference is authentic.

Can a version be edited after release?

Not under this contract. Changed material content under the same version is a conflict. Publish another version and preserve the earlier snapshot. An exact duplicate can remain idempotent historical evidence without becoming another observation or silently replacing the existing released value.

How do I compute a reproducible digest?

Validate a fixed schema, own its values, define ordering and encoding, then hash that declared representation. Include material scope and version fields. This example uses a schema-specific serialization and does not claim to canonicalize every possible JSON document under a universal standard.

Should JSON key order change the digest?

It does not under this schema because the loader constructs a fixed admitted value. Entry order is also immaterial here because the entry set has no precedence. Do not apply that rule to another schema whose list order affects behavior without documenting and testing the difference.

How do duplicate rules fail?

The loader rejects duplicate graphemes inside the snapshot's target scope. It also rejects duplicate JSON object keys during parsing, before an ordinary mapping could conceal an overwritten value. These are distinct conflicts and both need negative fixtures in a strict release boundary.

What does a regression fixture verify?

It verifies the stated software behavior on a defined synthetic input, such as stable serialization or rejection of a changed version. It does not establish pronunciation, listener preference or endpoint activation. Preserve positive and negative cases so rejecting all input cannot masquerade as correctness.

What still requires listening review?

Any claim about the actual sound, intelligibility or preference under the selected voice needs captured audio and an appropriate review protocol. Keep release and audio identities linked. A text diff can identify changed rules but cannot predict every acoustic effect of those changes.

Can a text diff predict acoustic behavior?

No. It shows declared content differences under a scope. Voice configuration, engine behavior and surrounding text can affect sound. Use the diff to choose review cases, then retain actual observations and uncertainty rather than turning a changed alias into a presumed quality improvement.

How does provider support affect a release?

The target interface must support the representation being selected. Record exact provider, endpoint, voice and relevant regional conditions in the adapter. A standards-compliant local artifact is not proof that every provider accepts it or uses the intended precedence during synthesis.

Why can region matter for hosted lexicons?

Some providers, including the documented Polly interface, scope stored lexicons regionally. A real adapter must select a resource under the correct configuration. This example uses synthetic target values and performs no remote lookup, storage operation or claim that a hosted lexicon was applied.

What evidence permits activation?

The owning workflow defines current activation authority, compatibility checks and review requirements. The example only records a reviewed snapshot when supplied digest and scope match. Historical retrieval or a passing test does not automatically grant current production selection permission.

How should rollback work?

Select a retained reviewed snapshot under the current release policy. Preserve its original content and evidence instead of editing a new version to resemble it. The example exposes retained snapshots but performs no actual activation, rollback transaction or remote deployment.

Should old versions be deleted?

Follow the actual retention and permission policy while preserving enough authorized evidence for reproduction and rollback. Historical availability and current selectability are different properties. A revoked version can remain restricted history without being eligible for new synthesis requests.

How do I record a removed pronunciation?

Create a new snapshot without that entry and emit the removed-key diff. Keep the earlier snapshot under the retention policy. Do not erase the old rule from history and then claim that a past synthesis request used the current dictionary contents.

What does the offline validator leave to adapters?

It leaves evidence authenticity, target support, listening review, current activation authority, remote operations, caching and final post-wait admission to their owners. Those boundaries should remain explicit. A small correct validator is useful without impersonating an entire production speech or release system.

When is a separate deployment system unnecessary?

When a bounded manual selection process can reliably preserve reviewed content, current authority and reproducible request identities. Start with that direct path. Add machinery only for a concrete operational need, while keeping the content, review and activation contracts understandable and independently testable.

Release a small reviewed change

Validate one synthetic alias snapshot, inspect its emitted diff, and confirm that changed scope or review evidence cannot reuse an earlier approval. Then capture the actual target audio under an authorized review process.

Keep release content immutable and activation explicit. That gives another engineer enough evidence to understand a pronunciation change, reproduce its selection and choose a retained reviewed version when a regression requires rollback.