fix(e2e): preserve accessible controls and deep drawer navigation

Preserve source provenance in REST-loaded resources so owning-snapshot
consumers retain provider context and host placement. Keep visible control wording in accessible names and retain readable command
and navigation text in both themes. Token fixtures now await mounted browser
bootstrap before handing control to the next journey.

Measure bounded platform table items together with their inline details so
scrolling down a tall drawer cannot unmount the resource being inspected.
Retain regression coverage for bootstrap timing, drawer state and scroll
extent, responsive controls, Patrol and availability, and align navigation
assertions with current read-only capabilities and composed resource queries.
Provider fixtures retain one authoritative inventory across transport updates.
This commit is contained in:
rcourtman
2026-10-24 21:41:34 +01:00
parent 074c3a4743
commit 58f9398a41
42 changed files with 757 additions and 128 deletions
@@ -15,6 +15,19 @@
## Purpose
Infrastructure Add shortcuts and update chips follow the frontend primitives
visible-label naming contract. Their names preserve `Add` or the displayed
update wording and append source or host context, while keeping the existing
installer destination and affected-agent scope. An Agent Doctor destination
does not replace the update chip's visible action in its accessible name.
Agent Doctor keeps evidence at its notice tone's full text contrast. Its
scrollable repair, inspection, removal and credential commands are native
keyboard focus targets with visible focus, so long commands remain readable
without a pointer; copy actions and execution authority are unchanged.
The command-access description in the installer must remain readable against
its consent panel in both themes through the shared semantic text tokens. The
operator needs that explanation before choosing host command authority.
### Command access is a named token-step choice, not a legacy option (21 October 2026)
The installer's command-access checkbox reads **Allow Pulse to run commands on
@@ -2830,7 +2830,10 @@ thought-signature, and provider-result normalization as Assistant and MCP —
destination in `AppLayout`'s tab lists shares the nav identity-stability
contract owned by `frontend-primitives` (`stableNavTabs.ts`): a websocket
state frame with structurally unchanged tab content must not recreate the
Patrol button DOM or its attention-count badge element. Patrol and the peer
Patrol button DOM or its attention-count badge element. Utility chrome
shares the app-shell light/dark accessibility proof, including
readable small count badges, without changing their attention or capability
sources. Patrol and the peer
utility destinations commit navigation without awaiting best-effort module
warming, so lazy-route work cannot make the shared shell drop or defer a
tap.
@@ -3334,6 +3334,9 @@ a new API state machine, queue contract, or verification-accounting field.
attention projection by severity, then actionable review posture, then
observation recency, but it must preserve every API-owned lifecycle,
evidence, protection, action, and verification fact in selected detail.
Attention-row control names expose the displayed record in source order,
through the Patrol and frontend primitives visible-label contract; rendering
must not replace those facts with a title-only accessible name.
The first-party `Inbox`, `Protection`, and `Activity` modes are a mutually
exclusive presentation boundary, not API query modes: Inbox consumes the
canonical attention projection, Protection consumes retained objectives,
@@ -1072,7 +1072,10 @@ responsive shell appear to ignore an operator.
App-shell navigation rendered by `frontend-modern/src/AppLayout.tsx` must
also keep decorative icon titles out of tab accessible names, so hosted and
self-hosted chrome announce the canonical tab label plus meaningful badge
counts rather than duplicating branded SVG titles. The Patrol-owned utility
counts rather than duplicating branded SVG titles. Small navigation counts
must retain readable contrast in
both themes; changing a theme must not replace a readable critical badge
with a lighter background that fails text contrast. The Patrol-owned utility
destination remains `Patrol` over the same `/patrol` route; it must not
alter hosted org bootstrap, entitlement loading, or commercial posture
loading. Its badge may consume only the existing Patrol attention summary.
@@ -3014,6 +3014,14 @@ artifact-selection behaviour.
deterministic token authentication after setup, and may use the server setup
API as a fallback only when the UI wizard fails to complete cleanly under the
managed hot-dev runtime.
A successful token probe and a protected workspace URL do not establish
completed browser bootstrap. Token and cookie reuse must wait for the mounted
authenticated app shell before returning control to a journey, so a pending
workspace redirect cannot overwrite its next deep link. Browser rejection
remains an authentication failure rather than a reason to accept the URL or
retry login. `tests/integration/scripts/fixture-auth.test.mjs` exercises the
actual token helper with delayed and rejected bootstrap; the runtime-home
onboarding browser contract also holds a real protected-state request.
The built `index.html` must not modulepreload lazy route chunks: the SRI
plugin runs with `preloadDynamicChunks: false` so cold start fetches only the
entry and its static vendor imports (route-level code splitting stays
@@ -6487,6 +6495,13 @@ truth inside Playwright helpers. `tests/integration/tests/helpers.ts` may
offer request trackers for browser-shell contract proofs, but those helpers
must observe page-originated traffic only and must not blur browser runtime
requests together with `page.request` or other direct API helper calls.
Provider inventory fixtures must own every inventory input consumed by their
journey. `tests/integration/tests/vmware-workload-fixture.ts` serves its canonical
REST snapshot with a mocked empty socket so a live frame cannot replace its VM
identity during drawer inspection. Its resource-detail and exclusion-integrity
browser proofs exercise read-only context and retain provider-admin prohibitions.
This synthetic fixture does not establish live vCenter or websocket acceptance.
The shared authenticated browser fixture must not report the default mock
runtime ready from inventory alone while historical chart initialization is
still running. When Core E2E requests the default mock-readiness gate, the
@@ -165,7 +165,23 @@ native QGA/backup or update-permission recovery.
shared history query and Docs with synthetic observations at desktop/phone
widths, including keyboard/touch guidance and same-node updates.
### Navigation labels retain their visible badge text (24 October 2026)
### Control names retain their visible wording
Action names include their visible text in source order. Context may extend a
short label, but must not replace it with a different action name. Infrastructure
group shortcuts retain `Add` before their source label; update chips retain
their visible count and update wording before the host context. Patrol attention
rows use their native button contents so severity, title, summary and observation
age remain part of the name as the displayed record changes.
Scrollable command displays are native keyboard focus targets with visible
focus. A sibling Copy button does not make the command's own scroll area
keyboard reachable.
Semantic muted text must meet normal-text contrast on the surface and hover
backgrounds where controls and disclosures use it in either theme. Dark muted
text uses slate-300 so slate-700 hover/consent panels remain readable; individual
callers must not compensate for an inaccessible shared token. The authenticated
browser accessibility proof covers both themes and an explicitly hovered
Patrol row as well as expanded installer and Agent Doctor states.
Desktop and mobile utility links include their visible label and badge numbers
contiguously, in source order, in the accessible name (WCAG 2.5.3). Mobile
@@ -1316,6 +1332,14 @@ the mounted window advances far faster than the real scroll position and drops a
group's rows mid-scroll. The controller samples several leading siblings and
keeps the tallest, so uniform tables still measure their real row height while
mixed group/content lists keep a content-scale estimate.
Native table rows additionally measure each mounted item's summary and any
`InlineDetailTableRow` marked `data-inline-detail-row` as one logical item.
Keyed measurements own prefix offsets, native spacer extent and pixel-to-item
lookup, so scrolling within a tall drawer cannot advance past and unmount its
owner. Offscreen items retain their last measured size until remeasured.
Resize and direct-child mutation observers update those measurements outside
the scroll/wheel path. The mounted-item budget and native touch path remain
unchanged. List surfaces retain representative height estimation.
Shared workload, node, Docker-host, and resource-drawer history presentation
must scope retained observations to the exact resource type, resource ID and
@@ -15,6 +15,16 @@
## Purpose
Attention-row buttons derive their accessible names from their visible content,
following the frontend primitives control-name contract. Severity, title,
summary and relative observation age remain available to assistive technology
in display order. A title-only override must not hide the other visible label
text; selection, keyboard review and detail focus restoration keep their existing
owners.
Mobile return controls retain `Back to list` in their contextual names. The
expanded resource-copy control retains its visible `Copy ID` name, including
after copying; the fuller explanation remains its tooltip.
### One-page Patrol setup on the Patrol page
When Patrol is blocked because no provider is connected, Pulse Intelligence
@@ -1777,6 +1777,13 @@ change may globally weaken the Task 03 lifecycle-state idempotency invariant.
state, mounted-row budgets, viewport sync, and reveal timing; it must not
fork the repeated `TableRow` / `TableCell` / surface-alt / click-containment
shell inside workload, infrastructure, or platform table render paths.
Bounded platform tables use keyed measured item heights, including their
native inline details, when mapping scroll pixels to their mounted runway
and spacers. Reading a tall drawer must keep its owner mounted without
disabling windowing or increasing the row budget. Measurements update on
resize and row mutation rather than adding per-row layout reads to each
scroll event. The mounted `PlatformWindowedRows.test.tsx` regression covers
retained editing state inside a tall drawer and its offscreen scroll extent.
Pod-mode workload filters must use the workload-owned
`K8s cluster` / `All K8s clusters` labels for Kubernetes context selection,
rather than a generic `Cluster` label that can be confused with Proxmox
@@ -1884,7 +1884,8 @@
"frontend-modern/src/api/__tests__/agentProfiles.test.ts",
"frontend-modern/src/api/__tests__/monitoring.test.ts",
"frontend-modern/src/components/Settings/__tests__/InfrastructureOperationsModel.test.tsx",
"frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts"
"frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts",
"tests/integration/tests/83-product-trust-accessibility.spec.ts"
]
},
{
@@ -4430,7 +4431,8 @@
"exact_files": [
"frontend-modern/src/__tests__/App.architecture.test.ts",
"frontend-modern/src/utils/__tests__/apiClient.issue1640.test.ts",
"frontend-modern/src/utils/__tests__/apiClient.org.test.ts"
"frontend-modern/src/utils/__tests__/apiClient.org.test.ts",
"tests/integration/tests/83-product-trust-accessibility.spec.ts"
]
},
{
@@ -4732,6 +4734,7 @@
"tests/integration/scripts/relay-mobile-token-helper.go",
"tests/integration/tests/helpers.ts",
"tests/integration/tests/runtime-defaults.ts",
"tests/integration/tests/vmware-workload-fixture.ts",
"VERSION"
],
"release_cycle_artifact_globs": [
@@ -5238,6 +5241,20 @@
"tests/integration/tests/16-dev-runtime-recovery.spec.ts"
]
},
{
"id": "browser-fixture-helpers",
"label": "authenticated browser fixture and onboarding handoff proof",
"match_prefixes": [],
"match_files": [
"tests/integration/tests/helpers.ts"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"tests/integration/scripts/fixture-auth.test.mjs",
"tests/integration/tests/62-runtime-home-onboarding-contract.spec.ts"
]
},
{
"id": "dev-runtime-orchestration",
"label": "dev runtime orchestration proof",
@@ -5264,7 +5281,6 @@
"tests/integration/README.md",
"tests/integration/scripts/managed-dev-runtime.mjs",
"tests/integration/scripts/managed-local-backend.mjs",
"tests/integration/tests/helpers.ts",
"tests/integration/tests/runtime-defaults.ts"
],
"allow_same_subsystem_tests": false,
@@ -5352,6 +5368,20 @@
"scripts/installtests/native_unix_contract_runner_test.go",
"scripts/tests/test_native_installer_contract_runner.py"
]
},
{
"id": "browser-provider-inventory-fixture",
"label": "authoritative browser provider inventory fixture proof",
"match_prefixes": [],
"match_files": [
"tests/integration/tests/vmware-workload-fixture.ts"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"tests/integration/tests/39-vmware-resource-detail-drawer.spec.ts",
"tests/integration/tests/41-vmware-phase1-exclusion-integrity.spec.ts"
]
}
],
"match_files": null
@@ -5583,7 +5613,8 @@
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/__tests__/App.architecture.test.ts",
"frontend-modern/src/__tests__/AppLayout.test.tsx"
"frontend-modern/src/__tests__/AppLayout.test.tsx",
"tests/integration/tests/83-product-trust-accessibility.spec.ts"
]
},
{
@@ -6131,6 +6162,7 @@
"frontend-modern/src/components/Workloads/__tests__/DrawerDiskListCard.readings.test.tsx",
"frontend-modern/src/components/Workloads/__tests__/HostDrawer.filesystem-readings.test.tsx",
"frontend-modern/src/components/Workloads/__tests__/NodeDrawerOverview.updateEvidence.test.tsx",
"frontend-modern/src/features/platformPage/__tests__/PlatformWindowedRows.test.tsx",
"frontend-modern/src/features/proxmox/__tests__/ProxmoxNodesTable.test.tsx",
"frontend-modern/src/utils/__tests__/proxmoxUpdateEvidence.test.ts"
]
@@ -1943,6 +1943,10 @@ recovery scope, or a storage/recovery-owned secret source.
when the policy exposes them, and they must not add edition- or
license-sniffing of their own to re-suppress or re-expose paid prompts.
3. Route canonical storage/recovery resource selection through `frontend-modern/src/hooks/useUnifiedResources.ts` and the owning `unified-resources` contract
REST projection retains its validated source list on `resource.sources` as
well as platform metadata, so owning-snapshot consumers keep the same
provider context as websocket consumers. That provenance describes inventory;
it supplies no fresh storage observation, protection verdict or recovery grant.
That shared hook now also projects resource `clusterId` through the shared cluster-name helper, so storage and recovery links keep the same cluster-context label as other unified-resource consumers instead of rebuilding a local fallback chain.
That shared hook plus the adjacent websocket/store adapter path must keep
realtime transport merges canonical for storage/recovery consumers too:
@@ -23,6 +23,15 @@ and sort the complete canonical change table while startup and ingestion wait.
## Purpose
### REST snapshots retain canonical source provenance
`useUnifiedResources` preserves the validated source list on the canonical
resource as well as its platform metadata. Owning-snapshot consumers such as
Workloads derive their provider context from `resource.sources`; a REST refresh
must not erase vCenter placement, host grouping or source-specific behavior
that the websocket projection supplies. The REST mapping regression and the
authoritative VMware drawer browser fixture cover that consuming boundary.
### Drawer history follows current source authority
Resource drawers withdraw current chart fallbacks and cached live-only replies