Pre-v5 material is provenance, not a compatibility promise. v5.0 and v5.1 were private design generations; the synchronized public language/toolchain line was chosen at v5.2. Migrate by preserving intended behavior while rewriting source into the current canonical grammar, then let the current checker expose every unresolved boundary.
1. Inventory before rewriting
Record entry files, module roots, inputs, outputs, faults, host effects, and golden transcripts. Separate actual Topaz from Rust-, JavaScript-, or framework-shaped examples. Do not infer semantics from an old page merely because a form looks familiar; consult the current SPEC and canonical examples.
2. Normalize the recurring forms
- Rewrite
mut letaslet mut,[T]asArray<T>, and old function-type spellings as(T) -> U. - Use
..restin list patterns,...args: Tfor variadic parameters, lambdas for anonymous functions, andselfonly in the admitted receiver declaration. - Replace string indexing/
.lengthwith scalar APIs; use typed registry templates and{expr}interpolation. - Keep recoverable failure in
Result, absence inOption/nullable shapes, runtime faults loud, and cleanup indefer/admittedusing. - Rewrite modules to the current
importandexportforms. Foreign tokens remain at a labeled build or conversion boundary.
3. Keep historical code historical
This is a legacy spelling and must stay in a text fence, never a canonical Topaz fence:
mut let names: [string] = ["Ada"]
let first = names[0]The current shape makes mutation, collection type, and bounds behavior explicit:
let mut names: Array<string> = ["Ada"]
let first: Option<string> = names.get(0)
print("{first}")4. Rebuild the proof, not just the parse
Run topaz check --language-version 5.6, then execute reference fixtures with run. Build only the targets you intend to ship and compare declared observables. Recheck module initialization, iteration order, scalar string positions, numeric faults, resource cleanup, and host-profile boundaries. Parsing alone does not prove migrated behavior.
5. Know the automation boundary
The CLI migration command covers only named historical package/source transitions; it is not a general pre-v5 semantic translator. Long-form guides may contain framework APIs and foreign grammar that no automatic rewrite can validate. Compare the example in Classic, then adapt it using the current language.
Migration exit gate
A migration is closed when the current checker passes, required outputs are recaptured, admitted backends agree over the declared tests, no Hard Stop remains in canonical fences, current docs links replace legacy authority, and every unsupported behavior has a loud disposition.