Who books tours? — the one question on the Channels step

Three plain answers, each starting a connect that already ships. And the one vendor button that was removed rather than re-captioned.

16 Sept 2026 · branch lane-c/who-books-tours · captured against propflow-stage · at 6a57bc8a2e+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 · Nothing is pre-selected

Three answers, and none of them chosen for the admin. resolveToursSectionState returns selected: null on a plain visit on purpose — pre-selecting My leasing team does because it is the do-nothing answer would record a choice nobody made, and the wizard would carry it into a company's tour routing.

No vendor buttons render anywhere yet. A row opens into its buttons only once it is the answer, which is what keeps this from being the four-button screen it replaced.

Asserted in the captured DOMWho books tours? · I do · Tours land on my calendar. · A shared calendar · My leasing team does · You can connect a calendar any time from Settings.. All three answers and the Settings line are in the captured DOM. What is absent is the point: no 'Continue with Microsoft' or 'Continue with Google' appears until a row is chosen.

Nothing is pre-selected

2 · "I do" — both vendors, one click each

Choosing the row reveals both vendors, and pressing either goes straight to that vendor's consent screen: personCalendarAuthorizeUrl(vendor, '/onboarding/channels?tours=me'), one hop, no interstitial. There is deliberately no pick-a-vendor-then-press-Connect second step — that shape shipped on this very screen in #7651 and was pulled the same night.

The ?tours=me marker on the return URL is load-bearing: the person callback answers with calendar_connected, the same param the per-building shape reads as this building's calendar connected. Without the marker the two landings are indistinguishable.

Asserted in the captured DOMI do · Tours land on my calendar. · Continue with Microsoft · Continue with Google. Both vendor buttons are in the captured DOM once 'I do' is selected — the expansion is real DOM, not a style change. The row's reassurance line is deliberately NOT asserted: it belongs to the CONNECTED state, and this capture is the unconnected one. (The first draft of this spec claimed it here and the tool refused the run — which is the tool working.)

"I do" — both vendors, one click each

3 · "A shared calendar" — a sentence, not a disabled button

This capture is the per-building shape, which a local dev server resolves to. A company that runs a calendar per building has no shared one, so the row states where those calendars live. A greyed-out button would have implied a shared calendar exists and is merely unavailable; there isn't one.

On a centralized company this row instead shows Google only. Review round 1 found that a Microsoft button there would start the company inbox consent — company-channels/connect asks for ['mailbox','calendar'] for any non-Google company. Narrowing the scopes does not fix it either: fresh-channel-token.ts's scopesFor has only two lanes keyed on attachment.personId, so a calendar-only company grant would refresh with the Mail.*.Shared scopes its consent never covered and die AADSTS65001 about an hour later — the Western Slope failure of 2026-09-11/14. The button was removed, not re-captioned.

Asserted in the captured DOMA shared calendar · Each building has its own calendar. Connect them from each building's settings.. The per-building sentence renders in place of any button. Nothing matching 'Continue with' appears inside this row.

"A shared calendar" — a sentence, not a disabled button

4 · "My leasing team does" — nothing to do now

The answer that connects nothing renders nothing to connect with. Each teammate is offered their own calendar at first sign-in, on /welcome/calendar, which already ships.

This is the answer that made the offer stamp matter: an admin who picks it never visits a calendar screen, so calendarWelcomeOfferedAt has to be written by this screen rendering — which is what the follow-up PR does.

Asserted in the captured DOMMy leasing team does · Each teammate connects their own calendar when they sign in. Nothing to do now.. The row's full caption is present and no vendor button renders under it.

"My leasing team does" — nothing to do now

5 · The whole step, at phone width

The section below the channels list, under the step's single Continue. The rows are their own bordered blocks, so they stack without the grid collapse the teammates row needs — worth seeing at 390px because the two vendor buttons inside an expanded row are full-width and could otherwise overflow it.

Asserted in the captured DOMWho books tours? · I do · You can connect a calendar any time from Settings.. The section renders in full at phone width with no horizontal overflow.

The whole step, at phone width

Captured anonymously, and why there is no production frame

Same route lane A uses: a local dev server serves the wizard unauthenticated (src/middleware.ts short-circuits outside production, and the wizard layout only runs its redirects when a session exists), so these frames are the step as a customer meets it. The identities this tool normally signs in as cannot reach it — the smoke account is platform_admin, whom the wizard layout redirects to /dashboard, and the seeded non-staff identity is already onboarded.

A production frame is impossible by construction, not by effort. scripts/ui-capture/target-guard.ts asks the running server which DDB table it is bound to and refuses anything outside an allowlist of anonymized-by-construction sources, and forbids widening that allowlist in its own header. That is deliberate: this page is a URL that outlives the conversation, and the same capture against prod would carry real people's names.

Two states are therefore not here. Connected · <vendor> needs a real vendor round trip; it is pinned instead by the rendered suite and by a read-only run against the real Fairhaven row in propflow-prod, whose shared calendar is genuinely microsoft/connected and which the section reads as answered. And the centralized shape's Google-only row needs a company session the local gate cannot mint.

PropFlow Docs