A polished new feature is not useful if earlier records can no longer be read. I wanted a visible contract for when and how data changes rather than an unexplained automatic upgrade.
This new code turns fixed synthetic version-one values into a version-two proposal. It neither migrates a company database nor overwrites a restore file; conversion preview is separate from the earlier restore checks.
A renamed field must not hide a semantic change
Synthetic version-one rows contain id and qty. The version-two proposal moves qty into quantity and adds status with an explicit draft default. This is an independent mapping experiment rather than an extracted company storage format. Without an agreed default, identical older records can acquire different new meanings.
Draft is not universally correct for real historical data. A production conversion must establish the old workflow semantics and identify decisions needing review. The example fixes its mapping openly and explains the added field. It does not ask a converter to infer unknown business facts or silently classify actual transactions.
Validate the entire input before proposing output
Document keys, schema, row collection and each row’s fields, identity and quantity are checked first. Unknown fields, unsupported versions and duplicate identifiers fail. A valid first row followed by an invalid second row produces no partial proposal, leaving the source untouched.
This does not implement atomic writes across database rows. It is a pure calculation, and the test verifies that failure leaves the input unchanged. A production batch migration needs its own failure location, continuation rule and record of applied portions. Whole-input failure in a small array is a useful boundary, but it is not an operational migration engine.
A proposal is not an applied migration
migration_preview returns source schema, target schema, changed, proposed and applied. Applied is always false. Calculating a new structure and committing it to a service are distinct facts. The function chooses neither a backup location nor a production restore target; the response shape prevents a preview from masquerading as a completed update.
Real application requires review, write authorization, a fresh expected-version check and preservation of the source. If another operator changes the record before application, an old proposal cannot simply replace it. This package performs no application transaction and creates no operational change history. Those missing stages are documented as integration responsibilities.
Do not transform current data repeatedly
Version-two input is validated and copied, with changed false. Feeding an earlier proposal back into the function yields equivalent proposed contents. The test checks that repetition does not add duplicate fields or recalculate quantity, while the original version-one input remains preserved.
Idempotent preview does not establish reversibility. New states can eventually carry meaning that an older schema cannot express. There is no reverse converter here; compatibility with an older code release is checked separately. Repeating a transformation safely and restoring an earlier representation without loss are different properties and should not share one success label.
Check compatibility before application
Tests cover mapping, an invalid second row, unknown versions, unexpected fields and repeated preview. A cross-module check also feeds the proposed schema into an older release’s compatibility contract and observes that it cannot read it. A correctly shaped conversion is insufficient if the code about to run cannot interpret its result.
The shared deliverable is synthetic code and local evidence, not a completed production migration or rollback. Adoption needs source preservation, actual restore rehearsal, release ordering and application authorization. I expose a format change as a reviewable proposal instead of hiding it behind the successful appearance of a new screen feature.