Summary
A valid follow-up surfaced during review convergence on PR #546 and was intentionally kept out of that PR because it belongs to the separate Local-First / Yjs reconstruction path rather than the import-to-bootstrap hydration boundary.
The current project/import contract permits arbitrary string entity IDs, including names that collide with Object.prototype such as:
__proto__
constructor
toString
PR #546 hardened cold-boot/import normalization so those IDs survive as own properties in prototype-safe EntityState records. The Local-First readback path still reconstructs character/world collections differently.
Current affected path
services/localFirst/projectDoc.ts currently rebuilds collections in readProjectDoc() using the project adapters, conceptually:
charactersAdapter.addMany(
charactersAdapter.getInitialState(),
readEntities<Character>(doc.getMap('characters')),
)
and equivalently for worlds.
The validated Codex review finding on PR #546 observed that with enableLocalFirstSync and an entity ID such as __proto__, RTK/Immer can attempt to treat that key through the ordinary initial {} entity map and fail with behavior equivalent to:
Object.setPrototypeOf() cannot be used on an Immer draft
The shadow-sync/readback path can therefore fail for project data that the main persistence/import contract now explicitly preserves.
Reference review: PR #546, Codex discussion #discussion_r3897865402.
Required invariant
Every entity ID accepted by the authoritative project/import persistence contract must round-trip through the Local-First shadow document without being dropped, reinterpreted as an inherited property, or causing reconstruction failure.
This includes empty/whitespace string IDs if they remain part of the established accepted contract, as well as prototype-colliding IDs.
Preferred bounded fix
Do not redesign the Local-First architecture.
At the Yjs → ProjectData reconstruction boundary, reuse the existing prototype-safe EntityState construction authority introduced by the #546 lineage (or an equivalent shared helper) rather than rebuilding through an ordinary {}-backed adapter initial state.
Preserve:
- canonical
ids ordering;
- exact entity contents;
- duplicate-ID rejection/fail-closed behavior where applicable;
- ordinary UUID/string IDs;
- existing Local-First shadow/verification semantics.
Do not reject previously accepted IDs merely to avoid the object-key collision.
Regression matrix
At minimum prove for both characters/worlds as appropriate:
Scope
This issue is specifically the Local-First/Yjs reconstruction compatibility gap.
It is not:
The PR #546 thread correctly classified this as a valid follow-up outside that PR's authorized scope. Track and fix it here so #546 does not need to expand into Local-First B1.1 internals.
Summary
A valid follow-up surfaced during review convergence on PR #546 and was intentionally kept out of that PR because it belongs to the separate Local-First / Yjs reconstruction path rather than the import-to-bootstrap hydration boundary.
The current project/import contract permits arbitrary string entity IDs, including names that collide with
Object.prototypesuch as:PR #546 hardened cold-boot/import normalization so those IDs survive as own properties in prototype-safe
EntityStaterecords. The Local-First readback path still reconstructs character/world collections differently.Current affected path
services/localFirst/projectDoc.tscurrently rebuilds collections inreadProjectDoc()using the project adapters, conceptually:and equivalently for worlds.
The validated Codex review finding on PR #546 observed that with
enableLocalFirstSyncand an entity ID such as__proto__, RTK/Immer can attempt to treat that key through the ordinary initial{}entity map and fail with behavior equivalent to:The shadow-sync/readback path can therefore fail for project data that the main persistence/import contract now explicitly preserves.
Reference review: PR #546, Codex discussion
#discussion_r3897865402.Required invariant
This includes empty/whitespace string IDs if they remain part of the established accepted contract, as well as prototype-colliding IDs.
Preferred bounded fix
Do not redesign the Local-First architecture.
At the Yjs →
ProjectDatareconstruction boundary, reuse the existing prototype-safe EntityState construction authority introduced by the #546 lineage (or an equivalent shared helper) rather than rebuilding through an ordinary{}-backed adapter initial state.Preserve:
idsordering;Do not reject previously accepted IDs merely to avoid the object-key collision.
Regression matrix
At minimum prove for both characters/worlds as appropriate:
__proto__round-trips Redux → Y.Doc → ProjectData;constructorround-trips;toStringround-trips;ProjectDocBinding.verify()reports no false drift for the supported IDs;Scope
This issue is specifically the Local-First/Yjs reconstruction compatibility gap.
It is not:
The PR #546 thread correctly classified this as a valid follow-up outside that PR's authorized scope. Track and fix it here so #546 does not need to expand into Local-First B1.1 internals.