Keep page state through a successful reconnect

A successful Blazor reconnect could remove an open install dialog even though the server retained the circuit and the material finished installing. The cause is the route-level error boundary in src/Memex.Portal.Gui/Routes.razor: its key was the RouteData object. The router creates a new RouteData instance when its parameters render again, including at the same URL. That reference change replaced the boundary and disposed the page, layout and their transient streams.

The boundary instead follows the navigation URI through its query, excluding the fragment. A same-URI authentication notification or same-page anchor navigation keeps the page and dialog; real path or query navigation resets the boundary, including recovery from a page error. Authentication and authorization still update normally outside that identity. The source change is confined to the route key and its injected NavigationManager.

The framework behavior is explicit in the matching ASP.NET Core 10.0.11 Router source: SetParametersAsync refreshes after its initial navigation (lines 117–165), and a matched route constructs new RouteData (lines 251–255). A router's same-path render is not proof of browser navigation.

Evidence that identifies the disposer

The Education PR 313 ci8314 run 34549749666 failed its existing 600-second terminal-dialog assertion. The browser had shown nine of 22 roots before a timeout/reconnect. Backend copies and final manifest-path writes continued, while updates to the previous status stream were rejected as disposed. No document reload or new circuit was recorded. That artifact did not contain the full manifest body, so neither the final Repair button nor a manifest-path log substituted for all 22 copy assertions.

A separate fixture defect also mattered: coupon acquisition already starts installation, but the old Education setup helper returned at “You're subscribed. Setting up…” and closed its context. The measured Get could therefore overlap acquisition. Education commit 4150307 now completes that operation, verifies the final manifest, and uses normal confirmed Uninstall before fresh Get; its local 28-case journey and final-source setup 1-case run passed. This does not repair reconnect.

Controlled local observations separated the lifecycle defect from that setup and from slow copy:

Existing Debug logging identified the exact next edge in a further paired control. On 2026-09-11, the retained circuit came UP at 02:31:51.096531Z. At 02:31:51.096742Z the router matched the same /DataModeling path. At 02:31:51.097146Z the existing layout-area component reported hadStream=True keepStream=True. At 02:31:51.097153Z the renderer disposed component 15, the outer ErrorBoundary, followed by RouteView, MainLayout and ApplicationPage. The layout-area components then disposed their streams. At 02:31:51.143569Z a new layout-area component first rendered and opened a new remote stream on the same circuit. This is parent component replacement, rather than a layout-area reference change initiating retirement. The full layout teardown also explains the observed side-panel/Monaco disposal and reinitialization.

No new instrumentation or application code was needed to obtain that trace. The 8339 preview's appsettings.json was backed up byte-for-byte, only existing logger levels were raised, then the original bytes were restored in a finally guard after capture. Its configuration SHA256 was 5f76af68efff05771103f41a9d27c1cb247450cf3e97ce861a3ce9e15945dec5 before and after; container, image, PID, StartedAt and zero-restart count were unchanged. Target categories were LayoutAreaView, NamedAreaView, Workspace, synchronization streams, both LayoutAreaHost logger names, Circuit, AccessContext, and ASP.NET Core Router/Renderer/Circuits. MemexPortalComposition.cs:90–93 loads JSON with reloadOnChange:true; fresh Debug messages proved activation before the fault.

Runtime and validation boundaries

The controlled runtime was the existing isolated 8339 core 508aeb6 candidate, not production or sealed ci8314. Portable-PDB checksums from the running Blazor, Hosting.Blazor and Portal.Gui assemblies matched all 11 traced renderer files at Plugins 2591c6fb, 8ee8a192 and 109d3ff. This bounds the source comparison; it does not declare the entire candidate closure equivalent to CI.

RoutesReconnectStateTest exercises production Routes with the real router, route view, error boundary and authentication cascade using the standard HtmlRenderer. The browser-size sensor and application/layout leaves are replaced through IComponentActivator. The probe owns transient dialog text and records disposal. Assertions require authentication changes/logout to reach the real AuthorizeView, same-URI state retention, path/query navigation replacement and error-to-new-route recovery. No copied key helper, test executable reference, grants or mesh data are involved. The old production key failed both same-URI authentication cases: the previously rendered page was disposed and replaced. The three real-navigation/error controls passed. The URI key passed all five cases with the identical test source. The full existing Memex.Portal.Gui.Test suite then passed 268/268, zero skipped, and both strict Release builds reported zero warnings and errors. Validation used an isolated checkout of core 45306a33 (the ci8314 framework source), SDK 10.0.400 and runtime 10.0.11. It did not replace any preview DLL or module.

PR 1638 review identified one additional navigation case: the full URI includes a #fragment, so anchor-only navigation could still replace the boundary. A further actual-Routes regression failed on that full-URI key: the page was disposed on fragment navigation. The test also requires the dialog to survive a subsequent authentication notification and the real AuthorizeView to show the changed identity. The final key uses Uri.GetLeftPart(UriPartial.Query), preserving path and query while excluding the fragment. Navigation controls with %23 in either the path or query prove those encoded characters remain meaningful route changes. On identical regression source, the full-URI baseline was 1 failed / 7 passed; the final key passed 8/8. The final whole GUI suite passed 271/271, zero skipped, with a strict Release build reporting zero warnings/errors. These results supersede the earlier five-case/268-case correction receipt.

The final repository gates passed: 89 node types clean, with no debt, breaks, fingerprint drift or unverifiable types; 261 nodes valid across 60 packages; all manifest locks current; and git diff --check clean. These gates supplement the executed GUI tests; the renderer source itself is compiled and tested by the strict Memex.Portal.Gui.Test build above.

Receipts are /private/tmp/roadmap-routes-regression-baseline/routes-baseline.trx, /private/tmp/roadmap-routes-regression-green/routes-green.trx and /private/tmp/roadmap-routes-full-suite/routes-full-suite.trx. The baseline directory preserves the actual old Routes source, test source and executed GUI/test DLLs and portable PDBs. An initial harness setup error (missing FocusOnNavigate JS service) was corrected before the causal baseline; it is not counted as a product regression result.

/private/tmp/roadmap-routes-regression-green/receipt.json records the unchanged regression source between red and green, source/binary hashes, commands and the three actual TRX verdicts. Repository gate logs are /tmp/roadmap-routes-node-compile.log, /tmp/roadmap-routes-shape.log and /tmp/roadmap-routes-lock-check.log.

The final fragment-case sources, executed baseline DLLs/PDBs and red/green TRXs are conserved in /private/tmp/roadmap-routes-fragment-red/ and /private/tmp/roadmap-routes-fragment-green/; the latter's receipt.json records the exact source/binary comparison. Full-suite output is /private/tmp/roadmap-routes-fragment-full/routes-fragment-full.trx. Final repository gate logs are /tmp/roadmap-routes-fragment-node-compile.log, /tmp/roadmap-routes-fragment-shape.log and /tmp/roadmap-routes-fragment-lock-check.log.

Local raw receipts, without authentication material in this page:

This is a local correction under validation. Production delivery requires the normal compiled portal publication/deployment and an actual reconnect/dialog acceptance on the delivered build.

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.