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
The Dock.* pins are held at 12.0.0.2 because upgrading to 12.1.0.2 breaks deserialization of already-persisted ("legacy") dock layouts. Dock 12.1.0.2 (upstream PR danipen/Dock #1107 "Fix Owner reference-cycle handling across all serializers") widened the System.Text.Json $id/$ref reference-tracking scope so it no longer resets per-list. Legacy layouts written by 12.0.0.2 contain massively duplicated $id values (a per-list-isolated-scope artifact — the very bloat from #1335/#1334), which the 12.1.0.2 deserializer rejects. Impact: we cannot take the Dock upgrade (and its native #1107 reference-cycle fix that supersedes our #1335 workaround) until legacy persisted layouts are migrated. This is also a real user-facing MIGRATION risk: any user's already-persisted 12.0.0.2 layout would fail to load after the upgrade, not just the test fixture.
Serialization stack: features\Phantom.Workspaces\ViewModels\DockLayoutCanonicalizer.cs — CreatePreserveOptions() (:42-52) sets ReferenceHandler.Preserve + our WorkspaceDockTypeInfoResolver; IsPreserveFormat(json) (:60-61) = json.Contains("\"$values\""); Deserialize(json) (:68-83) uses the preserve path for new files but for LEGACY files (no $values) falls to new DockSerializer(typeof(ObservableCollection<>), new WorkspaceDockTypeInfoResolver()).Deserialize<IRootDock>(json) (:81-82) — THIS is the path the 1334 fixture takes. WorkspaceDockTypeInfoResolver (...\WorkspaceDockTypeInfoResolver.cs) applies $type discriminators and orders collections ($id) before scalar refs ($ref) via ApplySerializationOrder (:144-157).
PRIMARY — legacy-layout migration pre-pass in DockLayoutCanonicalizer.Deserialize before the legacy DockSerializer.Deserialize call (:81): run a JSON-level transform that either (a) STRIPS all $id/$ref metadata from legacy-format JSON (only 3 $refs total — negligible sharing lost; Dock rebuilds from $type + inline objects), or (b) RENUMBERS $ids to be globally unique and rewrites the 3 $ref targets. Pure JSON transform, no Dock-internals dependency. Crucially this also unblocks any USER's already-persisted 12.0.0.2 layout on upgrade, not just the fixture.
Regenerate the fixture under the current preserve write path (SerializeCanonical) as a secondary safety net so the test also covers the new persisted format; keep the legacy fixture behind a dedicated migration test.
When implementing, check whether Dock.Serializer.SystemTextJson 12.1.0.2 exposes a ctor/option to opt out of the new reference tracking (possible temporary escape hatch, not the long-term fix).
Verification: after migration + pin bump, WorkspaceDockPersistence_LoadRealLayout1334_HealsTo11Docs passes under 12.1.0.2; full dock persistence/tab-switch/layout suites green incl. stability run.
Expected Tests
Follow WorkspaceDockPersistenceTests convention Subject_Scenario_ExpectedOutcome (existing examples at :65,:93,:112,:135).
Summary
The Dock.* pins are held at 12.0.0.2 because upgrading to 12.1.0.2 breaks deserialization of already-persisted ("legacy") dock layouts. Dock 12.1.0.2 (upstream PR danipen/Dock #1107 "Fix Owner reference-cycle handling across all serializers") widened the System.Text.Json
$id/$refreference-tracking scope so it no longer resets per-list. Legacy layouts written by 12.0.0.2 contain massively duplicated$idvalues (a per-list-isolated-scope artifact — the very bloat from #1335/#1334), which the 12.1.0.2 deserializer rejects. Impact: we cannot take the Dock upgrade (and its native #1107 reference-cycle fix that supersedes our #1335 workaround) until legacy persisted layouts are migrated. This is also a real user-facing MIGRATION risk: any user's already-persisted 12.0.0.2 layout would fail to load after the upgrade, not just the test fixture.Root Cause
features\Phantom.Workspaces.Tests\WorkspaceDockPersistenceTests.cs:132-150(WorkspaceDockPersistence_LoadRealLayout1334_HealsTo11Docs). Loadsfeatures\Phantom.Workspaces.Tests\Fixtures\dock-layout-1334.json(~118 KB real bloated layout from Restored multi-region ProportionalDock inner layout renders both regions headerless — real tabs present but header items missing (duplicate-instance last-writer-wins wiring from persistence leak + non-atomic restore of unresolvable descriptors) #1334) viaDockLayoutCanonicalizer.Deserialize(json)(:140) →Canonicalize(..., liveTabIds:null)(:143) →MigrateBaseDocumentDocksToWorkspaceContentDock(:144); assertsAllDocs.Count==11, oneMvvmControls.RootDock, zero rawMvvmControls.DocumentDock, no orphan windows (:146-149).features\Phantom.Workspaces\ViewModels\DockLayoutCanonicalizer.cs—CreatePreserveOptions()(:42-52) setsReferenceHandler.Preserve+ ourWorkspaceDockTypeInfoResolver;IsPreserveFormat(json)(:60-61) =json.Contains("\"$values\"");Deserialize(json)(:68-83) uses the preserve path for new files but for LEGACY files (no$values) falls tonew DockSerializer(typeof(ObservableCollection<>), new WorkspaceDockTypeInfoResolver()).Deserialize<IRootDock>(json)(:81-82) — THIS is the path the 1334 fixture takes.WorkspaceDockTypeInfoResolver(...\WorkspaceDockTypeInfoResolver.cs) applies$typediscriminators and orders collections ($id) before scalar refs ($ref) viaApplySerializationOrder(:144-157).dock-layout-1334.jsonhas NO$valuesenvelope (→ legacy branch) and contains 233$idtokens but only 13 distinct values —$id="1"appears 105 times,$id="2"101 times,"3"13,"4"4,"5"2 — with only 3$reftokens total. Pre-Dock-layout persistence leak: ActiveDockable/DefaultDockable/FocusedDockable and orphan floating Windows are inline-cloned every save (DockSerializer bypasses ReferenceHandler.Preserve), compounding to 100+ WorkspaceDocument instances for 11 tabs #1335,DockSerializer.JsonConverterListserialized eachIList<T>in an ISOLATEDJsonSerializer.Serializescope, soReferenceHandler.Preserve's$idcounter reset at every list boundary (documented inDockLayoutCanonicalizer.cs:19-26andWorkspaceDockTypeInfoResolver.cs:23-26). Under 12.0.0.2 the deserializer mirrored that per-list scope so the reused ids were legal. Under 12.1.0.2, PR Crash: ObservableCollection reentrancy opening a tab — remove dock->Tabs order back-sync (Tabs order must be independent of dock) #1107 no longer resets the reference scope per list, so the first duplicate (105 objects sharing$id="1") throws'$id' metadata property '1' conflicts with an existing identifier. This is root cause (a), confirmed by direct fixture inspection; the dual-discriminator and "new objects gain $id" hypotheses are not needed to explain it.Affected Files
features\Directory.Packages.props(:17-21)features\; no submodule variant) — to move to 12.1.0.2features\Phantom.Workspaces\ViewModels\DockLayoutCanonicalizer.cs(:42-52,60-61,68-83,86-113)DockSerializerdeserialize path — migration pre-pass goes herefeatures\Phantom.Workspaces\ViewModels\WorkspaceDockTypeInfoResolver.cs(:128-157,144-157,195-208)features\Phantom.Workspaces\ViewModels\MainWindowViewModel.cs(:2840 write, :3583/:3600 read)features\Phantom.Workspaces.Tests\WorkspaceDockPersistenceTests.cs(:132-150)features\Phantom.Workspaces.Tests\Fixtures\dock-layout-1334.json$idsDesign / Fix (ordered options)
DockLayoutCanonicalizer.Deserializebefore the legacyDockSerializer.Deserializecall (:81): run a JSON-level transform that either (a) STRIPS all$id/$refmetadata from legacy-format JSON (only 3$refs total — negligible sharing lost; Dock rebuilds from$type+ inline objects), or (b) RENUMBERS$ids to be globally unique and rewrites the 3$reftargets. Pure JSON transform, no Dock-internals dependency. Crucially this also unblocks any USER's already-persisted 12.0.0.2 layout on upgrade, not just the fixture.SerializeCanonical) as a secondary safety net so the test also covers the new persisted format; keep the legacy fixture behind a dedicated migration test.Directory.Packages.props:17-21to 12.1.0.2 for all five Dock.* together (never a partial subset — Bump Dock.Avalonia and Dock.Avalonia.Themes.Fluent #1339 only moved 2 of 5, which desyncs the set).ApplySerializationOrdermachinery inWorkspaceDockTypeInfoResolver(and possiblyCreatePreserveOptions) MAY be simplified towardDock.Serializer.SystemTextJson's own serializer. Do NOT remove the heal/dedupe wholesale — it is still needed to reduce already-bloated persisted graphs (Dock-layout persistence leak: ActiveDockable/DefaultDockable/FocusedDockable and orphan floating Windows are inline-cloned every save (DockSerializer bypasses ReferenceHandler.Preserve), compounding to 100+ WorkspaceDocument instances for 11 tabs #1335). Note this simplification alone does NOT fix Review, validate, and merge all open Dependabot dependency-update PRs #1346 because the failing input is a pre-existing legacy file — option 1 is still required.Dock.Serializer.SystemTextJson12.1.0.2 exposes a ctor/option to opt out of the new reference tracking (possible temporary escape hatch, not the long-term fix).Verification: after migration + pin bump,
WorkspaceDockPersistence_LoadRealLayout1334_HealsTo11Docspasses under 12.1.0.2; full dock persistence/tab-switch/layout suites green incl. stability run.Expected Tests
Follow
WorkspaceDockPersistenceTestsconventionSubject_Scenario_ExpectedOutcome(existing examples at :65,:93,:112,:135).WorkspaceDockPersistence_LoadLegacyLayoutWithDuplicateIds_MigratesAndHealsTo11DocsWorkspaceDockPersistenceTestsdock-layout-1334.json(105×$id="1") deserializes under Dock 12.1.0.2 via the migration pre-pass and still heals to 11 docsWorkspaceDockPersistence_Dock1210Upgrade_DeserializesLegacyFixtureWithoutIdConflictWorkspaceDockPersistenceTests$idlayout does not throw the'$id' ... conflicts with an existing identifiererrorDockLayoutCanonicalizer_LegacyJsonWithDuplicateIds_StripsOrRenumbersReferenceMetadataWorkspaceDockPersistenceTests(or a canonicalizer-focused test class)$ids and intact$reftargetsRelated issues
DockLayoutCanonicalizer/WorkspaceDockTypeInfoResolver; upstream PR Crash: ObservableCollection reentrancy opening a tab — remove dock->Tabs order back-sync (Tabs order must be independent of dock) #1107 in 12.1.0.2 is the native fix that this upgrade adopts).dock-layout-1334.json).