App audit — a map of every page, and every gap

For Gera: a tree of what the app has, from the rail down to the API routes each page calls, with every gap five read-only auditors found on 2026-09-23. Tap a severity to filter, or search.

2026-09-23 · code at main 41ec47759f · code read, not run (three live checks, listed under Not checked) · feeds Phase 11 · Page optimizations

In five lines

  1. Checked: 124 pages (96 inside the app, 41 of them staff admin, plus 28 standalone sign-in, invite and onboarding pages) and all 489 API route files. Every finding is code read, not run.
  2. 6 S0 — another company's data: 2 reachable today, 1 destructive and possibly by design (needs a decision), 3 conditional (they need a login lookup to fail, a login with no company, or a stray building on someone's list).
  3. 37 S1 — wrong data or a wrong action, merged across the five auditors. Below that the tree lists 41 S2 picker-ignored, 37 S3 speed and 88 S4 polish gaps.
  4. Being worked tonight (in flight, not fixed): S0 cross-company, permissions, multi-org, building picker, popover layers, app speed, prospects white screen. Tonight's four list-page fixes (#10928, #10931, #10938, #10941) were re-read at main and hold (Costs: except its purchase-order queue).
  5. 11 S1 gaps fit none of those lanes and need an owner — they are under Fix first → "No lane yet".

"Already fixed" was wrong twice tonight. Work orders and Prospects were said to follow the company picker; both still go blank after a company switch, because the building dropdown never learned which company you picked. The auditors assumed nothing was fixed until they read it.

Fix first — every S0 and S1, deduplicated

Each item names the file and line at main, which lane owns it (lanes matched by name; the driver confirms), and which auditors found it. "Pages" jumps to the item in the tree.

The tree

Rail section → page → what is on it (tabs, drawers, dropdowns, detail pages) → the API routes it calls. The dot is the worst gap on that page. Every page links to its Phase 11 board row (admin pages share three group rows; standalone pages have none yet).

Not checked — an absence of evidence is not a pass

Nothing was run in a browser. No dev server, no screenshots, no two-tab reproduction. Every picker and stale-list behaviour is traced in code. Every finding is "code read, not run" except the three live checks named below.
The three live checks. (1) Production cache headers on dynamic pages, unauthenticated (Next overrides to private, no-store). (2) Production environment variable names (the MFA exemption is set; its value was not read). (3) A read-only production probe of thread keying: centralized company 49 conversations, 32 people, 1 person split across buildings; the two-building company 1,000 conversations (looks like a cap), 488 people, 0 split. The probe excluded unassigned and test rows, ran from the main checkout's data layer, and its exit code was not captured.
How far the S0s actually reach. Not measured: how many active customer logins have no company (vendor S0); whether the 30-second session cache persists across requests on the live runtime (fail-open S0); how guessable other companies' property, unit and tenant ids are (turnover S0); whether any login's building list names another company's building (mass-send S0 and the 31 building-only routes).
How far the S1s reach. Not counted: live Viewer and maintenance-tech logins, building-limited managers, logins in two companies, pending review items older than 30 days, residents shown as Month-to-Month because their lease end is missing. Runtime permission overrides (access inspector) were not read against production and could widen or narrow the role findings.
Security items to prove on stage first. The MFA enrol bypass (the auth library may refuse to mint that session; the code suggests it does not). An authenticated cache-header check on an admin page that carries cross-company data.
Downstream re-checks. Whether the turnover scope dispatch re-checks vendor engagement; whether the mass-send workflow re-checks scope; how work-order create validates turnover and vendor ids; the collections reminder send gates; whether picking a company also changes the session's home company (narrows the vendor-detail gap).
API cross-cut limits. The script looked one call deep; about 117 routes were cleared because it found a scope function and only about 15 were re-read by hand. "Follows the picker" there means the route calls the selection reader, not that its counts and dropdown options narrow. Write routes with an id were not opened one by one unless flagged. Clara's agent code, Lambdas, server components and server actions were out of scope.
Pages and parts not audited. Settings → Account; the Settings pages themselves (only their API routes); /login and /login/mfa beyond a skim; 3,000-line pages (pipeline lab, renewal detail, access inspector) were spot-checked; other detail pages (tenant, prospect, work order, turnover, unit, property, vendor) were not checked for the keep-cache-on-404 bug; whether Vercel previews run as production (decides whether /api/agents/* is open on previews); whether /calendar is module-gated; mobile layout.
Middleware relay. Not traced end to end, so it is not ruled out that something copies the old building cookie into the company selection.

How the counts reconcile

S0 and S1 above are merged by hand: the same gap found by several auditors counts once (the work-order "unknown caller" gap was found by two; the Prospects and work-order blank lists by two each). Where auditors rated the same gap differently, the higher rating is used and the item says so. S2–S4 are the distinct gaps listed in the tree. For comparison, what each auditor reported before merging:

AuditorS0S1S2S3S4Counted by
leasing01693 (+6 secondary)23per surface row
maintenance518910~25deduplicated within the lane
conversations-chrome0913711each finding once
admin-onboarding09911~24distinct findings
api-routes25322per finding (S1 covers ~52 routes)

S0/S1 by lane on this page:

LaneS0S1
S0 cross-company66
permissions08
multi-org03
building picker09
none011

Sources: scratchpad/lanes/app-audit/{leasing, maintenance, conversations-chrome, admin-onboarding, api-routes}.md and workspace-pages.txt, driver session 1fdb267c. Customer names are replaced with role labels.

PropFlow Docs