Imagine an inventory-notes app. You rename the stored field label to display_name and make category a required field. If all 30 old records still appear, is the job done? A count cannot answer that when identically named items belong in different containers and the app never collected their categories.
The first question is whether the record with each ID keeps its original values and links. This article explains how we designed that check and what preparation established. The measured experiment was NOT RUN. We stopped before measurement because the remaining full procedure was estimated to exceed the work allowance. We have no result showing that either approach preserved the data or was safe after interruption.
In this article
Write down what must remain true before requesting code
An ID distinguishes one record from another. Two records can have the same displayed name and different IDs. Here we are changing the name of the storage field, not the item name inside it. Two containers can share a name and still have different IDs, so a link must point to the right one.
We prepared a synthetic SQLite database with no customer information: 30 item records plus three container records. The table below shows seven records in six case groups, selected from the full expectations table. It samples names and container IDs, showing inputs and expected values, not observed migration results. Container IDs must also remain unchanged.
The Korean words in the stored strings mean “Shared” and “Item”; the English glosses are already part of the synthetic data. The planned conversion must preserve these stored values without translating them.
The full check was designed to compare the exact set of 30 IDs, names, notes including NULL-versus-empty distinctions, the new category, container links, and the containers' own values. Missing or extra IDs matter too. Seven examples cannot replace that full check. In this test data, 26 records are expected to link to a container and four to have none. A link to an existing but different container would change a record's meaning. SQLite's foreign-key check detects relationship-constraint violations. Checking the intended destination requires separate expectations such as these.
The expected new category is unknown for all 30 old records, with the reason “not collected in the previous version.” This is possible because this app's declared policy permits that state. If another workflow requires an authoritative category, it must confirm categories from an authoritative source or collect them again; if that is not possible, conversion can be stopped. This does not mean guessing categories from names or notes.
Here NULL means no stored value, while "" is a stored string of zero length. Those are states of the existing name or note. The new category's unknown explicitly records that it was not collected. It is also distinct from an “other” option that another app might offer; this example has no such category. SQLite describes constraints in its CREATE TABLE documentation. Our inference is that satisfying a constraint does not establish a value's historical truth.
What we planned to compare
The two approaches are deliberately specified teaching policies, not samples of code that AI typically produces.
- Same-column-name copy (N): copy only fields whose names match, so the renamed
display_name field is left NULL, and fill every required category with supplies. This is an allowed category value with no evidence behind it for these old records. Copy IDs, notes and container links, committing each copied record separately.
- Explicit mapping (P): specify
label → display_name, retain IDs, notes and links, and assign unknown to the new category. Group the table rebuild and copying changes into one transaction and define a rerun policy.
N's defects were declared in advance. P could still be implemented or checked incorrectly. The approaches change mapping, commit boundaries, rerun handling and other factors together, so the comparison cannot isolate each factor's effect. The unit of change described in SQLite's transaction documentation is separate from whether the chosen values are correct. Our design calls for copying into a new table and then dropping the old one; renaming a column does not always require one.
We prepared a fixed expectations table before migration code, with a separate checker to read the resulting data. Migration code must not use the answer table to construct its output or decide whether it passes. The setup for generating migration code, which was not run, still allowed access exceptions such as configuration and authentication files and parts of the runtime environment. We cannot describe it as research in which access to the answers was fully prevented.
The plan called for four disposable copies of the preserved baseline: one clean run and one interrupted run for each approach. The interruption point was fixed after inserting the fifteenth record into the replacement table, before dropping the old table. Each safely inspectable clean completed output and recovered interrupted copy would then receive at most one rerun with the same, unmodified code. None of those runs, interruptions or reruns took place. Even if the process-interruption test at that one point had been run, it could not have established behavior during power loss, hardware damage or failure after the old table was dropped.
Check the screen separately from stored records
The prepared screen-check design distinguished a screen querying the old field name, one querying the new name, and the old screen with only its read query updated. A query error alone cannot establish data loss. Conversely, a correct display after adapting the query would not prove compatibility with the unchanged old app.
After interruption, record the state separately from the next action. Returning to a complete old structure is not completion of the conversion. A partial or unreadable state needs its own description. Refusal without changes, a retry that completes conversion, and a no-op independently verified against the expectations table must be reported separately. This design assumed a maintenance period with other writes stopped and was limited to read-only screens. Concurrent writes from old and new apps and zero-downtime deployment were outside its scope.
What offline preparation established, and why measurement was not run
The preparation record for October 12, 2026 reports 19 passing offline tests of the baseline and checker. They checked that the 30 synthetic baseline records matched the table's inputs and that the separate checker detected deliberately altered names, categories, relationships, notes, and NULL/empty-text distinctions. The reference target database (the positive control) was built directly from expected values. It was not produced by converting the baseline, and these checks were not N/P scores. The recorded environment was Python 3.9.6 with SQLite 3.54.0 linked to Python.
An independent review on October 26 recorded matching file sizes and content fingerprints (hashes) for 144 candidate files and 34 passing offline test executions. The 34 included repetitions, not 34 distinct scenarios, and should not be added to the earlier 19 as a cumulative score. That review initially blocked readiness because the baseline file that should have been protected was writable in a fresh working copy. The selected baseline copy was subsequently made read-only, and checks of inputs fixed to recorded versions, permissions and file separation passed. Only the design was then frozen. Permissions and inputs must be checked again each time the setup is used.
The November 5 operating decision estimated that generation, execution and verification would require about 75 more minutes, while about 29 minutes of the standing 60-minute preparation-and-experiment allowance had already been used. These are work-budget estimates, not measurements of migration speed. The decision was to write a methods article because the planned full procedure was estimated to exceed the remaining allowance. No reduced measurement was run either; this does not show that every smaller check would be impossible. N/P code generation, actual conversion, interruption, reruns, verification of migration outputs and screen observations all remain NOT RUN. Offline preparation cannot fill those gaps.
The SQLite documentation descriptions above rely on the saved October 11, 2026 checks and require rechecking before publication.
The practical takeaway is a request, not a winning approach: “Put the old and expected new values for each ID side by side in a table. State the evidence for each new required value and the destination ID of every relationship. Separately specify when to retry or refuse after interruption, and how to check the screens. Identify failure scenarios outside the test and distinguish preparation checks from actual conversion results.” Reviewing the per-ID table returned by the AI first is this article's proposal. Whether the records actually survive with their meaning intact requires execution and recorded checks against it.
AI assisted with research and drafting. The author reviewed the final manuscript and directed the revisions.