You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two defects, one of which forces the shape of the fix:
No validation. Every other write path validates against the type schema; restore is the one door where arbitrary historical content walks back in unchecked. Usually the snapshot was valid when written — but "usually" breaks under in-place schema drift (the thing schemaHash exists to catch) or a buggy adapter, and restore is precisely a recovery path, where silent corruption is most expensive.
typeId is not restored, which makes honest validation impossible. Restore a pre-migration snapshot onto a record that has since migrated to @2, and the result is @1-shaped content labeled @2. Validating that against @2's schema would rightly reject it — so the fix isn't to skip validation, it's to stop mislabeling: restore the snapshot's typeId along with its content.
Tests: restore of pre-migration snapshot yields stale record at old typeId that migrateAll() subsequently heals; drifted snapshot rejected; missing version → StackNotFoundError
Problem
restoreVersion()writes a historical snapshot back with no schema validation and without restoring the snapshot's type:(
packages/core/src/stack.ts:579–583)Two defects, one of which forces the shape of the fix:
schemaHashexists to catch) or a buggy adapter, and restore is precisely a recovery path, where silent corruption is most expensive.typeIdis not restored, which makes honest validation impossible. Restore a pre-migration snapshot onto a record that has since migrated to@2, and the result is@1-shaped content labeled@2. Validating that against@2's schema would rightly reject it — so the fix isn't to skip validation, it's to stop mislabeling: restore the snapshot'stypeIdalong with its content.Decided direction
content+typeIdfrom the snapshot (hard dependency: RFC: replace lazy migration with explicit owner-driven migration + additive cross-app evolution #47'stypeIdfield onRecordVersion).StackValidationError(422 over the wire per APIAdapter collapses the error taxonomy: Stack* errors never survive the wire round trip #53's mapping).migrateAll()re-migrates it on next run. Same principle as undelete (Undelete API: the spec promises soft-delete recovery, nothing implements it #60): neither operation migrates; both may produce stale records; one mechanism heals both.adapter-localandadapter-api.Work items
Stack.restoreVersion(): restoretypeId, validate against snapshot's type,StackValidationErroron failureRecordVersion.typeId(and coordinates with One versioning rule: every record mutation bumps version and snapshots full prior state (associations and permissions included) #61's snapshot fields)StackValidationError(APIAdapter collapses the error taxonomy: Stack* errors never survive the wire round trip #53)migrateAll()subsequently heals; drifted snapshot rejected; missing version →StackNotFoundErrorRefs #47 (
RecordVersion.typeIddependency; stale-is-legal; migrations-are-app-code), #61 (snapshot contents and restore scope), #60 (shared no-migration-at-restore principle), #52 (server-side endpoint + fixtures), #53 (error round-trip).