A persistence bug rarely stays inside the function named save.
The data may enter through an import. It may be edited in memory, replaced by a snapshot, written to browser storage or a desktop filesystem, exported, copied into a backup, restored later, or moved through a migration. At every boundary, a different mistake can change what the user owns or which writer has authority.
That is why “we fixed saving” is too small a description for a data-integrity effort. The useful question is: what must remain true as this project crosses each boundary?
Start with a preservation map
WorldScript Studio’s #553 work is better understood as a set of connected invariants.
At ingress, imported or restored content needs an admitted representation. During editing, the application must know which project lifetime and storage authority its baseline belongs to. At durable commit, a replacement must not be confused with a stale write from the project it replaced. At export and backup, the application must preserve portable user data while excluding machine-local details. During migration, the old representation must remain recoverable until the new one is accepted.
Those are separate obligations. Passing one does not automatically prove the next.
For example, an export path and a snapshot path may both produce JSON, yet they have different contracts. Canonical export projects the current model into portable output. A stored snapshot may need to preserve an older but supported shape as the user’s recovery source. Reusing one policy for both can either leak local metadata or destroy recoverable information.
Bind the baseline to authority
A document’s identifier alone does not tell us whether a writer is still current. The same project can be replaced while an editor still holds older in-memory data.
The editor therefore tracks a replacement epoch alongside the storage target and authority. A baseline is meaningful only for that combination. After a restore or import, the new carrier is admitted only if it still belongs to the expected identity and epoch. The replacement baseline advances after the durable save commits, not merely because the UI displayed a new document.
This is the difference between “the screen now shows the replacement” and “the replacement is durably authoritative.” The latter is the point where future edits may safely build on it.
Give each representation a job
The code distinguishes a raw carrier from a structured project model and from newly serialized text. On the filesystem, exact source text can matter: parsing and serializing again may normalize formatting or lose unknown fields. IndexedDB stores a structured value, so it has a different exactness boundary.
The application’s canonical egress path uses a stored carrier when available, overlays fields the application owns, removes machine-local metadata, and admits the resulting portable representation. That is a deliberately narrow contract. It does not mean every representation is byte-for-byte identical everywhere.
The same distinction appears in backups. A backup can include project data and selected snapshots while excluding API keys. If one snapshot is unreadable, the backup service records that entry as unavailable rather than discarding the entire backup. That is a resilience choice, not evidence that every backup can be restored successfully.
Treat migration as a transaction
A migration that overwrites the only old copy before validating the new form has made recovery depend on the migration never being wrong. The filesystem migration instead preserves the exact legacy text first, validates the schema stamp, and uses a lock/incarnation check before the atomic replacement. If initialization fails, the desktop path does not silently fall back to a different storage authority.
These rules make migration observable and bounded: retain the old evidence, validate the candidate, verify authority, then replace.
Make the campaign auditable
A useful integrity review is a table of boundaries, not a slogan:
- What representation arrives?
- Which authority owns it?
- What identity or epoch binds the writer?
- What exact data must survive?
- What is intentionally projected or omitted?
- What happens when validation fails?
- What evidence proves the durable transition occurred?
The #553 lineage spans import, restore, canonical egress, snapshots, backup, filesystem migration, and stale-writer protection. It is not one bug with one fix. Some paths have explicit current safeguards; the repository’s acceptance record also names residual restore ingress work. That distinction belongs in the story.
“No loss” should therefore be read as an engineering objective with stated invariants and evidence—not a universal warranty. A precise claim tells readers which boundary is protected, by what check, and where the known limits remain.
Continue reading
Next in Engineering for No-Loss Data Integrity: Exact Text vs Reconstructed State: A Persistence Boundary Lesson looks at what must remain exact across a persistence boundary.
For the release-side version of the evidence-identity question, see Test the Artifact You Built, Not an Equivalent Rebuild.
Practical extension: close the preservation loop
A preservation campaign is complete only when the recovery path has been exercised against the same authority model as ordinary editing. For each boundary, record four things: the authoritative input, the transformation allowed there, the durable output, and the evidence that the output can be reopened. An import may be parsed into a model; a user-authored source file may still need its original bytes retained. A snapshot may be valid data but inadmissible for the current project lifetime. A backup may be portable while intentionally excluding host-specific secrets.
Use a failure table before implementation:
| Failure | Safe outcome | Evidence to retain |
|---|---|---|
| Write is interrupted | Prior committed state remains usable | transaction result and previous generation |
| Project changes during an async save | stale save is rejected | project identity and epoch |
| Import is malformed | no active state is replaced | parser error and untouched source |
| Restore is ambiguous | current data remains recoverable | preserved pre-restore copy |
Browser storage does not turn a successful request into a universal durability guarantee. Transactions can abort, quota can be exhausted, and best-effort browser data can be evicted. Make those conditions visible and test recovery from them. See IndexedDB transaction guidance and browser storage quotas and eviction.
Top comments (0)