New mass send: says when its lists fail, and drops a building from another company

p11-page-mass-sends-new, part 1

24 Sept 2026 · branch perf/mass-sends-new-scope · captured against propflow-stage · at 0af2fe2a29

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 · Start: a share link opens the compose on Birch Row, with the top bar on All buildings

Company Fixture Property Co picked, no building in the top bar, and a shared compose link naming Birch Row (?propertyId=appfolio-9002). The share link wins on first load, so the compose targets Birch Row.

Asserted in the captured DOM — Birch Row · Clara texts. The compose subtitle names the share-link building.

Start: a share link opens the compose on Birch Row, with the top bar on All buildings

2 · After (this PR): switch company with that link still open, and the compose drops Birch Row

Same start, then the company switcher in the top-left corner changes to a different test company: tick Fairhaven Residential (TEST), untick Fixture Property Co, then Done. The switch is a soft refresh, and the bar reads All buildings before and after, so on main nothing counted as a bar change and the compose kept Birch Row, a building of the previous company. Now the compose re-resolves against the new company's list and has no building. The form says so and Send stays disabled. The ?propertyId= is still on the address.

Asserted in the captured DOM — Choose a property in the top bar to see who this reaches. · Fairhaven Residential (TEST). The no-building state is on screen under the new company.

After (this PR): switch company with that link still open, and the compose drops Birch Row

3 · After (this PR): one building picked, the lists load, no notice

One company and one building (Birch Row) picked in the top bar. The audience-options read succeeds and Specific units is chosen. No failure notice shows; the probe below asserts that too, after a successful retry.

Asserted in the captured DOM — Everyone in the building · Specific units. The normal path renders; the failure notice is absent (checked by the probe below).

After (this PR): one building picked, the lists load, no notice

Before on main, the failure state (companion probe), and the tests

The failure state is simulated in a real browser. The capture tool deliberately cannot intercept requests, so a companion Playwright probe ran against the same branch dev server (stage, smoke user, commit 0af2fe2a29, same selection cookie). Every GET /api/mass-sends/audience-options answered 500 until the probe clicked Try again, after which the real read went through. Nothing else was intercepted, and nothing was sent.

Compose with the audience lists failed: the notice under the audience choices

{"audienceOptionsCalls":2,"noticeShownOnFailure":true,"noticeAfterSuccessfulRetry":false}

BEFORE, the same company-switch flow on main's logic. This is the same branch dev server with compose-default-property.ts temporarily restored to origin/main's version. It was hot-reloaded, captured, and put back, and the tree was clean afterwards. After switching to Fairhaven Residential (TEST), the compose still reads Birch Row · Clara texts…, a building of the company just left. Both strings were asserted in the DOM.

Before: after the company switch the compose still targets Birch Row

Also pinned without a browser: the company-switch fix, where a share-link building from the previous company is dropped. It is a pure decision (syncComposeProperty), pinned by compose-default-property.test.ts, which fails on the pre-fix code with expected 'prop-a' to be ''. When that rule fires, the page is the ordinary no-building state: "Choose a property in the top bar to see who this reaches.", with Send disabled.

PropFlow Docs