Skip to content

core(project-boundary): finish persisted schema/version verdict and retire observation-only shadow authority #553

Description

@qnbs

Context

The living docs/native/CORE-MIGRATION-LEDGER.md marks the Project state-shape compatibility adapter as an in-progress prerequisite before native Core/Qt admission:

  • features/project/coreBoundaryAdapter.ts already translates Redux EntityState/array shapes to the renderer-neutral Rust project model and preserves ID/order invariants;
  • parity fixtures and an observation-only desktop shadow caller exist;
  • the current Rust verdict is explicitly partial because unknown fields are not rejected/fully classified;
  • the boundary envelope currently synthesizes schemaVersion rather than consuming one persisted as authoritative data;
  • there is deliberately no production authority switch yet.

No dedicated open issue currently owns this remainder.

Goal

Turn the current proof into an explicit, versioned Project/Core compatibility contract that can safely support later authority migration and Qt, without importing Redux Toolkit shape into Rust Core or discarding forward-compatible project data.

Required decisions

1. Persisted version authority

Decide where the compatibility/schema version comes from and how it is persisted.

Do not let a synthesized current version make older/unknown persisted data appear already migrated.

Define:

  • version field/owner;
  • absent-version legacy semantics;
  • supported older/current/future versions;
  • upgrade/migration admission;
  • downgrade behavior;
  • error/recovery result when a version is unsupported.

2. Unknown-field policy

The current Rust model does not provide a complete verdict for fields outside its modeled subset.

Choose an explicit policy per boundary/evolution stage:

PRESERVE_OPAQUE
ALLOW_FORWARD_COMPATIBLE
REJECT_UNKNOWN
MODEL_AND_VALIDATE
OUT_OF_SCOPE_BUT_MUST_NOT_BE_DROPPED

The default must never be silent data loss.

If Core does not understand a field that TypeScript currently persists, an observation/round-trip path must either preserve it or decline authority rather than deserialize/re-serialize a narrower object and erase it.

3. Authority-switch admission

Define the gates that move the current shadow comparison from:

OBSERVATION_ONLY

to any production decision/write authority.

At minimum require:

  • complete field inventory for the admitted lifecycle;
  • compatibility/version verdict;
  • no-loss round-trip proof;
  • TS/Rust accept/reject parity where both remain active;
  • rollback/fallback behavior;
  • representative historical fixtures;
  • no Redux-specific types leaking into Core.

Entity-state invariants

Preserve the already-established contract for characters/worlds:

  • stable IDs preserved exactly, including all IDs accepted by the authoritative import/persistence contract;
  • declared order preserved;
  • duplicates fail closed;
  • missing ids/entity correspondence fails closed;
  • orphan entities fail closed or are handled by an explicitly versioned migration;
  • prototype-named IDs must not be lost through ordinary object maps (coordinate with local-first: preserve prototype-named entity IDs when rebuilding ProjectData from Yjs #548 where Local-First uses the same data contract).

Fixture/evidence matrix

Add/maintain fixtures for:

  • current full project;
  • representative historical projects;
  • missing legacy version;
  • supported old version;
  • future/unsupported version;
  • extra unknown fields at top level and nested modeled boundaries;
  • array and EntityState character/world forms;
  • prototype/empty/whitespace IDs according to the established compatibility contract;
  • duplicate/missing/orphan entity references;
  • optional fields;
  • large representative project;
  • round-trip no-loss comparison.

Use golden fixtures in both TS and Rust where transition parity matters.

Relationship to other work

Acceptance criteria

  • Persisted project schema/version authority is explicit and no longer synthesized in a way that can hide compatibility state.
  • Unknown-field behavior is explicitly defined and cannot silently discard persisted user data.
  • Full admitted project-field inventory exists for any authority switch.
  • Historical/current/future-version fixtures exercise TS/Rust compatibility.
  • Array/EntityState conversion remains lossless for valid data and fail-closed for malformed correspondence.
  • Shadow verdict remains observation-only until all authority-switch gates pass.
  • A rollback/fallback path exists for any future Core authority transition.
  • Qt can use the same project contract without Redux Toolkit coupling.
  • CORE-MIGRATION-LEDGER.md row 9 is updated whenever the authority state or compatibility policy changes.

Non-goals

  • moving Redux reducers into Rust;
  • forcing strict unknown-field rejection when preservation/forward compatibility is the safer admitted policy;
  • implementing R-15 encryption here;
  • redesigning import UX;
  • beginning Qt vertical slices before the binding roadmap gates permit it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions