Four refusal states under a two-company selection

What a person sees on every work-order surface and in the palette when the top-left filter holds two companies: a plain instruction to pick one, never an outage.

14 Sept 2026 · branch ui-s-d-p2a-search-work-orders · captured against propflow-stage · at 8c0f9adbcf+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.

1 · The four surfaces, refused by name, on one fixture page

The real WorkOrderTable, ApprovalQueue, AutoApprovedList and CommandPalette from this branch, mounted inside the real workspace layout. Their two fetches — /api/work-orders* and /api/search* — are answered with the exact body the route tests pin for a two-organization selection: 400 { error: 'needs_one_org', organizationIds: [a, b] }. Everything else reaches the dev server untouched.

Why the copy is an instruction and not an error. Before this PR each of these surfaces fell into its generic failure arm on any non-2xx: the table read Unable to load work orders. Retrying... forever, the two queues read Failed to load…, and the palette reported a search failure to Sentry — for a person who did nothing wrong and whom refreshing cannot help. The picker is multi-select, so a two-company selection is one click away. Each surface now clears its rows (keeping the previous selection's rows on screen under the new one is the founder-reported bug itself) and says what to do. This is the same branch ConversationsLog grew in #8548, on the three maintenance surfaces plus the palette.

The palette notice is reached by typing a query: the section's actions fill the Find box with leak, which is what makes the palette fetch at all.

Asserted in the captured DOMPick one company to see work orders. · Pick one company to see the approval queue. · Pick one company to see auto-approved work orders. · Pick one company to search. All four refusal strings are in the DOM after the fetches resolved — one per surface. None of the pre-PR failure strings ("Retrying...", "Failed to load", "Search didn't complete") is asserted absent by the tool; read the picture for that.

The four surfaces, refused by name, on one fixture page

How this was reachable at all, and what the picture cannot show

The stage smoke account holds no Person role rows, so no signed-in route on this dev server can hold a two-organization selection. The capture therefore mounted the real components on an uncommitted fixture route inside the worktree, with a page-local fetch shim answering the two APIs with the refusal body. The components under the camera are exactly this branch's files; the route and the shim are not part of the PR and do not exist on main.

What this proves: the client half — each surface renders the refusal as an instruction. What it does not prove: that the server emits the refusal for a real two-org selection — that half is pinned by src/app/api/search/__tests__/route.selection.test.ts and src/app/api/work-orders/__tests__/route.selection.test.ts (400, the org set listed, zero entity reads).

PropFlow Docs