Replit: before returning to a checkpoint, decide what goes back with it
Replit brings conversation, app development and publishing together. Checkpoints help users return to earlier project files and context after a problematic change, with an explicit choice about restoring development data too.
Break down the problem and try your own design and implementation first. Then explore the reasoning to check states, edge cases, trade-offs, and ways to test your approach. The guide offers a reasoning path, not a complete implementation.
How can a snapshot preflight avoid partial recovery?
Scenario
Design a recovery preflight for your own project tool. Given current and target snapshots, validate the complete target and return a new state with file differences. Any error must leave current state unchanged. Discuss an in-memory pure function only: no disk writes, database requests, network recovery, concurrent modifications or crash recovery.
Inputs and terms
- The target must be a non-null object containing a files array and devDatabaseId. Each files item must be a non-null object with nonempty string path and blobId fields. A blobId is an opaque content-store identifier, not a filename. The store guarantees that an ID always means the same content and identical file content uses one canonical ID. The store and current snapshot are already validated and readonly; the target is untrusted. Ignore other fields.
- A path is a case-sensitive relative path separated by
/. Segments may contain only ASCII letters, digits, periods, underscores and hyphens, but an entire segment cannot be.or... Empty segments, leading/trailing slashes and backslashes are invalid. Every item is a file; directories are implicit. Within one snapshot, duplicate paths are invalid, as are filessrcandsrc/a.jstogether.srcandsrc-old/a.jsdo not conflict. - Each snapshot contains at most 100,000 files; total path characters across both snapshots are at most 5,000,000. Missing/non-array target files or invalid items are errors. An empty array is valid and means no target files. Target devDatabaseId must be a string or null. restoreDatabase must be a boolean.
- Every target file blobId must exist in the readonly store. With restoreDatabase=true, target devDatabaseId must be non-null and exist in the store; use it on success. With false, target database content need not be retrievable; keep the current ID. Upstream protocols already validate stored content and types. Do not recalculate hashes or validate database semantics.
- Success returns a new snapshot and changes array. A path present only in the target is added; only in current is removed; in both with different blobIds is modified; emit nothing otherwise. Sort by ASCII lexicographic path order. Entries contain path, kind, beforeId and afterId, using null for an absent side. Report the database result separately. Do not modify either input or the store.
- Locate errors by level: use target for a top-level error; the relevant object/field path plus reason for files-container, count-limit or total-path-length errors; and the field name for devDatabaseId or restoreDatabase errors. These have no associated file item, so do not invent an index. An individual file-item error needs its real array index and field, such as files[2].path. Duplicate or ancestor/descendant conflicts need both real indices. Report only the first error discovered; no priority between different errors is prescribed. Failure returns no committable new snapshot or partial differences and leaves current file and database references unchanged.
Start with this example
Current files are src/page.js → A and src/data/seed.json → B, with development database D2. Target files are src/page.js → C and src/data → D, with database D1. A, B, C, D, D1 and D2 exist. List file differences and database results for restoreDatabase=false and true. Replacing an old directory with a target file is valid: current and target must not be combined into one validation tree.
Then independently append src/data/more.json → A to the target; replace C with missing Z; or set target database ID to null. Explain success or failure under both option values and what must remain unchanged on failure. Also set the target to null, or change only restoreDatabase to the string "false", and locate each error. Indices are zero-based: the appended third item conflicts with the original second item, so identify indices 2 and 1.
Deliverables
Propose validation order, a path-conflict structure, difference computation and an all-at-once new-state result. Trace the examples and give at least five invariants with counterexamples. Let n be total file count across both snapshots and L total path length; analyze time and extra space, separately accounting for ID reading/writing/comparison. You may discuss JS Map or Rust collections, but provide no complete implementation or line-by-line pseudocode.
Explore the reasoning
- Validate in a separate workspace. Check the top level, array, items, paths, IDs and option. Object/field paths locate top-level, container and option errors; only file-item errors use actual indices, and path conflicts retain both. Keep the store readonly and expose no new state or partial differences before all required checks pass. With false, still validate devDatabaseId as string or null without requiring it in the store.
- Detect conflicts within the target by path segments. A trie can detect passing through a file node or marking a node with descendants as a file; a repeated terminal path is a duplicate. Preserve input indices. Plain string prefixes can wrongly reject
src-old. Do not insert current files into the target tree and manufacture cross-version conflicts. - Separate file differences from the database choice. Index current and target paths; determine added, removed and modified from presence and canonical content IDs. True uses the target database ID; false keeps the current one. Prepare all return objects before delivering one successful result. Atomicity here means no partial pure-function output, not distributed restoration.
- Trace the directory-replacement example. Lexicographic output is
src/dataadded(null→D),src/data/seed.jsonremoved(B→null), thensrc/page.jsmodified(A→C). False yields database D2; true yields D1. The appended child conflicts with target filesrc/data; missing Z fails both options; null target database succeeds with false and keeps D2, but fails with true. A null target locates target; a string option locates restoreDatabase; the appended path locates files[2].path and files[1].path. All failures preserve inputs. - Check invariants and costs. Success contains the complete target file set without ancestor-file conflicts; each difference path appears at most once in deterministic order; false preserves the database; identical snapshots have no file differences; failure respects readonly inputs. Run counterexamples independently, including an empty target and backslashes. Trie validation is O(L); expected-constant indexing permits O(n) set operations, but path hashing/comparison incurs character costs. Comparison-sorting k differences takes O(k log k) comparisons, each possibly scanning a common prefix; this is not path-length-independent O(n log n). Extra space includes O(L+n) paths/indexes and result references, without copying immutable blob contents; separately count actual ID-character costs.