A platform admin's org switcher lists every switchable customer, truncates each to one row, scrolls — and the dashboard routes read the pick instead of bypassing it.
14 Sept 2026 · branch ui-s-b-part1 · captured against propflow-stage · at 28de8dcf4d+local
Data note. Every name and balance below comes from propflow-stage, which is anonymized by construction (ADR-0097/0110 — phones in the impossible +1000 NPA, emails @example.test). The identical capture against prod would carry real tenants' names and balances and is not safe to attach anywhere, which is why the generator refuses to run against one.
The stage smoke login is platform_admin through the @propflowai.co domain override and holds NO role row anywhere — exactly the login that used to see one org (or, as PR #8301's evidence recorded, "No organization"). The grant now enumerates every switchable customer, so the panel lists all eleven stage customers. Each row is a single line: the label span carries white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0 and the full name rides on title.
What to look at: "Fairhaven Residential (TEST)" is one row, not two; the list is bounded (max-height: 280px; overflow-y: auto) and scrolls rather than growing the rail.
Asserted in the captured DOM — Fairhaven Residential (TEST) · JPCO · Western Slope Property Management · Propflow Property Management · Reshape Verify Property Group. Five of the eleven stage customer names asserted in the captured DOM after the panel opened — including the one Gera saw wrapped.
Fairhaven Residential (TEST) picked. This is the exact string Gera saw wrapped onto two rows; the corner now truncates it to one line at the header's width, 12px, and the full name is on hover. The single-org branch used to put the text straight into a flex box capped at 168px with no white-space rule — a direct text child of a flex container clips without an ellipsis, which is why the rule lives on an inner span.
Asserted in the captured DOM — Fairhaven Residential (TEST) · OCCUPANCY. The corner label is the picked org and the page renders beside it.
Before this PR the corner could name JPCO while /api/dashboard/stats answered for the whole platform. The platform_admin short-circuit in readDashboardScope is deleted, so a staff request is scoped by its address or cookie like anyone else's.
Review round 1 found the other half of that sentence missing, and this capture pair is the answer to it: the routes answered per-org, but DashboardDataProvider — mounted in a layout whose whole job is to survive navigation — carried nothing about the selection in its fetch dependencies or its sessionStorage keys, so the previous company's numbers stayed on screen under the new company's name for a 30s poll. Compare the shelf here with the next section's.
Asserted in the captured DOM — JPCO · OCCUPANCY · MONTHLY REVENUE. The corner names JPCO and the metric shelf has rendered — the numbers beside it are what this org's roster produced.
The same shelf under a different company. This is the capture the first review round was right to say was missing: five server-side route tests and six DOM-asserted captures all stopped at the route boundary, so nothing in the proof chain could see that the client never re-asked. scopeKey(selection) now leads both sessionStorage keys and sits in both fetch dependency arrays and both cache-restore effects, and dashboard-refetches-on-org-change.test.tsx pins it by rendering — its failing message under the mutation is literally the previous org's occupancy under the new org's name.
Asserted in the captured DOM — Western Slope Property Management · OCCUPANCY · MONTHLY REVENUE. A different company in the corner, the same shelf rendered for it — the pick is the read scope on both halves now.
Both sections above are hard loads at two addresses, and review round 2 was right that they prove nothing about the navigation the corner actually performs. This is that navigation: land on JPCO with its shelf rendered, open the corner, tick Western Slope, untick JPCO, press Done — a soft router.push to the same pathname. Nothing reloads.
It took three fixes to make this picture true, and each was invisible to the one above it. The committed selection was a prop of a layout Next does not re-run on a searchParams-only push, so it was frozen at mount; the refetch raced the selection cookie the edge writes during that push; and four client-side caches on this page were keyed without the scope, so the effect re-ran, hit the previous company's entry and skipped the request. The intermediate states are recorded in the commit message — including the one that is the defect's own portrait: the corner reading "Western Slope" over JPCO's $341,012.
What to look at: every card. This is the same page as the previous section, reached by clicking instead of loading, and it renders identically — the Leasing Pipeline went from JPCO's 30 available and 98 prospects to Western Slope's zeros.
Asserted in the captured DOM — Western Slope Property · No units in view yet · No rent recorded in view yet. After an in-app switch with no reload: the new company's name AND the new company's body, matching the hard load above card for card.
Two organizations selected. The route refuses by name (400 needs_one_org, ST-136: "two orgs on one dashboard is two walls, not one wide read") and the client now renders the route's own sentence in the error banner instead of "Unable to load dashboard data" plus a Sentry event. The previous org's numbers are dropped rather than left under a two-org label.
Asserted in the captured DOM — 2 organizations · Dashboard stats answer for one organization at a time.. The corner reads two organizations and the banner carries the route's refusal, verbatim.
The rail collapsed to its icon width. The corner shows initials at 13px inside a 40px cap; the label span keeps the same one-row rules, so a long name cannot push the expand control out of the rail.
Asserted in the captured DOM — OCCUPANCY. The page is intact beside the narrowed rail.
Phone width. The rail is an off-canvas drawer here, so the corner is behind the hamburger. Opened, the drawer's corner shows the same one-row label with a long org name picked.
Asserted in the captured DOM — Fairhaven Residential (TEST). The drawer's corner names the picked org on one row at 390px.
What two rounds of review found that these pixels had not. Round 1: the first capture set stopped at the address and the corner — every stat card was a skeleton — and that gap was exactly where the blocking defect lived, so the two one-org sections now wait for the shelf to render. Round 2: those sections are still hard loads, and the defect was in the SOFT navigation the corner performs. The in-app switch section is the answer, and getting it to render took three fixes stacked on each other: a committed selection frozen as a layout prop, a refetch racing the cookie the edge writes during the push, and four client caches keyed without the company. Each was hidden behind the one before it.
The picture worth keeping is the intermediate one, recorded in the commit message rather than shipped here: after the first fix the corner read Western Slope while the shelf still read 87.5% and $341,012 — one company's name over another company's money, which is the failure this whole row exists to prevent, arriving through a door nobody had checked.
What the pixels settled from the start: the login PR #8301's evidence recorded as "No organization" now holds all eleven stage customers. A staff login with no role row defaults to the first company that actually HAS buildings, so a first sign-in is not a blank dashboard. And the two-org refusal reads as a sentence in the page's own error banner — the first time needs_one_org has had a rendering anywhere.