# Unified state and private preview recovery

Use this reference when a deployed preview has APIs that work in isolation but the browser shows stale ranges, duplicate controls, unavailable views, or generic errors.

## Diagnose before redesign

1. Run a **metadata-only live API probe** against the effective private preview session. For each required endpoint record only HTTP status, safe error code, duration, response byte size, and result count. Never record values, titles, snippets, paths, IDs, tokens, or database rows.
2. Probe current, previous, historical-with-data, and empty calendar ranges plus exact date-window series requests.
3. If direct API probes are green while UI behavior is broken, investigate browser state ownership, route parsing, session bootstrap, feature-profile generation, and stale-response races before changing database/API contracts.

## Single-owner browser state

A complex browser surface should have one owner for selection, range, resolution, mode, chart, events, and selected day. Legacy code may remain as an isolated fallback, but it must have no parallel visible DOM, event listeners, or mutable state in the active profile.

For a shared date range:

- derive presets from the server-authoritative day and timezone;
- make inclusive windows explicit (for example, 30 days is today minus 29 through today);
- retain the range across search, selection, visibility toggles, reload, and browser history;
- send explicit bounded `from`/`to` values even for “all”;
- expose one range-change event/subscription rather than several private variables.

## Route-parser ownership

Every client-side URL parser must first determine whether the URL belongs to its namespace. An Explorer parser must return “not mine” for valid Day/Record routes before validating Explorer-only keys. It must never delete or normalize another router's parameters. Only malformed URLs owned by that parser may be fail-closed and cleaned.

Acceptance-test:

- valid deep links, reload, back, and forward for every router;
- duplicate/invalid parameters fail closed only in their own route;
- shared state restoration does not trigger write actions.

## Calendar and record resilience

- Synchronize the server day/timezone before the first calendar month is chosen; a generated bundle date and live server date can differ.
- Empty months still render a complete operable grid.
- Distinguish no data, session failure, timeout, and API failure.
- Project record summaries through named fields only; never generic `Object.values()`/object dumps.
- Scope errors to one tab, include a safe code and retry control, and keep other tabs usable.

## Browser-matrix profile alignment

Before the one-shot full matrix, map every spec to the **complete generated profile it asserts**. A spec that tests global search plus Explorer must run under the full preview profile, not an Explorer-only subflag. Validate this mapping with one focused group before declaring the matrix release-ready.

Retired legacy specs should be explicitly marked as superseded and their replacement contracts named. Do not resurrect parallel UI merely to keep obsolete tests passing.

## Private smoke accounting

A browser session bootstrap may legitimately return a non-200 success such as 204. Treat only HTTP statuses of 400 or greater as API errors. Browser history can intentionally abort superseded read requests; filter recognized abort/cancel failures, but require zero other request failures.

The final private smoke should report only technical counts/booleans: one session bootstrap, search terms exercised, selected-series count, range kind, calendar-cell counts, view usability, API response/error counts, external-request count, and relevant request-failure count.

## Descriptor-backed document smoke

For private previews that expose reviewed document originals, verify the complete real path without logging health content:

1. Bootstrap the same-origin browser session and paginate the document list.
2. Validate only public enum/status fields and count their distribution; never emit opaque IDs, titles, snippets, filenames, paths, or URLs.
3. Pick one item whose projected `original_status` is `available` internally, then require aligned list → detail → `HEAD` → `GET` results.
4. Open that same item through the real viewer and assert only visibility plus absence of path/filename leakage.
5. Report technical booleans and counts only: available-original count, HEAD/GET success, viewer opened, overflow, console errors, and external requests.

Do not treat a successful `HEAD` as a capability token: `GET` must perform the complete descriptor-based validation again. In smoke automation, use an actual stable product selector (for example the rendered viewer class or an accessible heading), not a guessed `data-*` hook.

## Strict-CSP third-party asset gate

Locally bundled libraries can still violate CSP without making an external request. Bundlers may inject `data:` fonts, source maps, or runtime style loaders even when the feature uses custom controls and never needs those resources.

- Attach console/page-error listeners **before navigation or feature activation**; require zero CSP and resource errors after opening the feature.
- Scan the emitted bundle for `data:` font payloads, external URLs, and source-map references.
- If an unused embedded resource is removed, update byte count, gzip size, SHA-256, license metadata, and the browser regression assertion together.
- Re-run the full release gates against the modified candidate. A late asset edit invalidates the earlier release-candidate matrix even when visuals appear unchanged.
- If the prescribed release commit was already pushed and amend/force-push are forbidden, use a transparent follow-up fix commit, push normally, atomically redeploy only the affected V5 asset, and re-run the private smoke.

## Release evidence hardening

Persist the fallback checksum and deployment timestamp in the rollback manifest **before** copying or restarting anything. This preserves before/after evidence even if readiness verification fails after the files were already installed. Readiness must use the effective private bind, not assumed loopback, and `active/running` alone is insufficient.
