🎧 Podcast

One model for every operating shape

Two hosts · 14 min 26 sec · 10 chapters

Full transcript, and how the audio was made

A medium-length two-host walkthrough of the proposal below, synthesized from the transcript with ElevenLabs' two-speaker dialogue voices (host + guest). Regenerate any time by re-running the render script over this transcript and re-dropping the .m4a.

Download .txt

Why this exists, and the bar it has to clear

PropFlow already sells to one kind of customer well: a single building with its own phone number, its own mailbox, its own calendar. Camellia is that shape. But the customers arriving now are not that shape. Western Slope is a company that runs eighteen scattered single-family homes off one number and owns none of them. Situs is a centralized company with its own database. ConAm manages a building it does not own. There is a floating leasing assistant who covers two buildings and belongs to neither. The question this document tries to answer is whether all of those can be the same system with different rows — or whether each new shape means another migration.

Fede set the bar in plain words. Never have to migrate again. Cover single properties, multiple properties, centralized, decentralized, hybrid, people who rotate, people who are assigned, one calendar or many, one line or many. Scale from the setup that exists today. And, quote, “I want proof.” So this is not a whiteboard sketch. It is a recommendation with five tests attached, an adversarial review that tried to break it, and a harness that has to go green.

One thing to say up front, because it governs how you should listen to the rest: almost none of this is decided. Fede and Gera walked it together on the evening of September sixth and took no hard decisions. Exactly one thing is settled, and only for the voice agents: one shared set of agents for every client, never cloned per property, and when a line points at a company, Clara asks which building. Everything else is proposed. Gera is running his own investigation at the code level and owns the implementation — the “how.” This is the “what,” and it gets corrected wherever his investigation shows it is wrong.

The whole idea, in one picture

Here is the entire model. There is a company, which is the customer and the wall between customers — that part already exists and does not change. Underneath it, optionally, a group: a named handful of buildings, a region, a cluster that shares a line. Most customers have zero groups; it exists so that adding a level later is inserting a row, not changing the schema. And underneath that, the building: one property, or one scattered-site home. The building stays the storage partition for its knowledge, its settings, its tours and work orders, because that is the one coupling in the code you cannot relax without physically moving rows.

Now the important move. A phone line, a mailbox, a calendar, the PMS login, a policy value, a vendor roster, an escalation owner — every one of those becomes an attachment on a node in that chain. Same row type at every level. And here is the payoff: the only difference between a single building, a centralized company, and a hybrid operator is which box the phone line is attached to. Camellia has the line on the building. Western Slope has one line on the company above its eighteen homes. The hybrid operator has one line on a group and separate lines on two other buildings. Three customers, one model, three places the line sits.

That is why “centralized” and “single building” stop being modes. They were never really different systems; they were the same system with the line in a different spot. So the property record's operatingMode field — the flag that says what kind of customer this is — gets retired. There is no customer-type flag, no branch per shape, no agent cloned per mode.

Address claims and the one resolver

Two mechanisms make that safe. The first is the address claim. A phone number or a mailbox belongs to exactly one node, and the database enforces that when the row is written — not a convention, a constraint. When a customer leaves, the claim is released and can be taken again. Compare that to today, where two buildings can silently list the same number and the winner depends on load order. That is the kind of bug that texts one customer's resident from another customer's number, and the claim is what makes it impossible rather than unlikely.

The second is the resolver. One function walks building, then group, then company. The nearest explicit value wins. Every answer it returns says which node set the value, who set it, and when. And if it cannot name a company, it refuses — no company, no answer, no reads at all. It costs at most three queries per resolution, with the company's attachments cached for sixty seconds, and it has to fit inside the ring-time budget the voice webhook already lives under. The reason this matters: there are seven different ways settings get resolved in the code today. This replaces them with one, modeled on the one resolver that already works well — the preferred-vendor resolver. So it is not a new invention; it is generalizing the pattern that already earns its keep.

Who runs it, who owns it, who sees it

This is the part every earlier proposal missed, and it is worth slowing down on. The morning's proposals, and all five of the panel designs, put the owner inside the operator's records as a label. That answers “group my buildings by owner.” It does not answer Fede's actual question, which is: a company logged into its own account, seeing a granted slice of a building that a different company runs.

Take Yale 25 Station. JP and Co runs it and owns it. ConAm manages it under contract. Three logins should see three different things: JP and Co's owner sees Camellia and Yale, because it owns both. ConAm sees only Yale, the building it manages. Camellia's on-site team sees only Camellia. The way you get that cleanly is a dated relationship row per fact — “runs,” “owned by,” “managed by” — and then every login's view is derived from those relationships at read time, deny by default. A manager gets the operational view of what it manages and nothing more. An owner gets reports. Anything beyond that is explicit. And because the view is computed from the live relationship rather than stored, it ends the moment the relationship ends.

The honest dependency here, stated plainly: today the dashboard decides what a login sees from a list on the login record, not from staff roles, and a company admin can still see every building on the platform because a July decision to fix that has not shipped. That read-side permission work is a prerequisite, and the rule the plan sets is blunt — no customer's people get a login until it is done.

A call on a company line

This is the one piece that is actually decided, so it is the clearest to walk. Someone dials the shared 970 number. The number's claim names a node. If that node is a building — Camellia's case — the building is known at pickup, and nothing about the call changes; the “which home” question is never even asked. But if the node is a company or a group, the building is a modeled “unknown” state. The resolver walks up for the shared values — hours, policies, calendar — and the same Clara, the same single agent, is told to ask which home. Once the caller names one, it is validated to be inside that company before any tool runs.

Two corrections from the review are baked into that now. First, the voice platform will not accept a tool parameter that is both stamped by the server and typed by the model. So the building is not “demoted” to a proposal the model fills. Instead the bound tools gain an optional “which property” text field the model fills from the conversation, and the server resolves it inside the company or refuses. Second, the resolved company travels with the request as a required argument, so a tool that has no company refuses with zero reads. Email gets the same unknown-building state: a company mailbox message the roster can't narrow asks or escalates — it never scores every building on the platform. And the two dangerous shortcuts today go away: the voice tools trusting a model-filled property id outright, and an unmapped number quietly adopting the caller's own building.

People and their calendars

A calendar belongs to the person, never to the building. That is not an aesthetic choice — it is how Cal.com and Deputy model it in production, and how multi-site healthcare scheduling works. So a centralized company like Situs has three leasing people, each with their own work calendar, assigned at the company and covering all its buildings, and tours rotate across the three. The floating assistant at Yale has her own calendar, is assigned to two buildings, and availability checks her calendar first, then the building's, then the manager's. Assignment is just a row: at one building, at several, or at the company.

The review call sharpened one thing here. Scheduling is code, not the model. Tour assignment across rotating people is modeled like an on-call rotation — a schedule that lives at the company or the building, resolved deterministically — and the model only ever calls one “schedule a tour” interface. It never picks a calendar. That shrinks what has to be evaluated. And one safety rule the review added: a calendar reachable from more than one company — the floating assistant's, when she works at two customers — receives busy blocks only. Today PropFlow writes the prospect's name, phone, income and Clara's notes into the calendar event; the other company's connection would fetch those fields. So the detail stays in PropFlow and the event just links to it. One prerequisite this surfaced: production refuses Google calendars right now — the adapter is half-built and a guard blocks it — and Situs runs on Google Workspace. That is one of the open decisions.

What “proposed” means, and the honest limits

The document is careful about what it is not claiming, and this is the section to actually pay attention to. The single most important limit: knowledge isolation weakens from structural to resolver-enforced. Today, one building cannot read another building's learned answers even by mistake, because the building is the database key and a pinned test asserts against that key. Under inheritance, that guarantee becomes “a function does it correctly.” The company wall stays structural — the anchor carries the company — but sibling-building isolation becomes a rule a resolver enforces. That is the feature you want for a portfolio, and a genuine downgrade for a multi-building customer. It should not be smoothed over, and the plan rewrites the pinned test to assert the walk only ever visits the building's own ancestors.

A few more, quickly, because they are the kind of thing that bites in week one. Failing closed looks like a regression: a number or mailbox that used to guess a winner will now refuse, and the operator has to be warned before the flip or it reads as a break. Two operators on one building is simply refused — if both partners in a joint venture want their own Clara on one address, that is two building records. And the design rides on top of some live bugs it does not pretend to fix: two voice-tool routes with different gates, renewal tools skipping an ownership guard on one route, permission overrides that are platform-global with no company key. Those are named openly as separate fixes.

What the adversarial review changed

Before this went out, eight attackers each took one lens and tried to break the draft — under-built for the onboardings, leaks, migration realism, “is this five proposals stapled together,” voice-platform feasibility, and a lens that threw thirteen future customer shapes at the entity model. Seventy-nine findings. Each went to two independent verifiers told to refute it against the code and default to “not material” when unsure. A finding survived only if both agreed it was real. Twenty survived. And notably, the “customer 50” lens that tested thirteen future shapes produced zero survivors — the entity model itself held.

What the twenty changed is instructive. One false sentence about conversations got corrected: the draft assumed a company-line conversation could live in a special partition every per-building query would still see, and four lenses showed it would actually be invisible on every per-building screen. So conversation birth was pulled earlier in the plan — a conversation now knows its company before any line is bound to one, because that window is exactly where a cross-customer identity pool would form. The review also named a pile of surfaces the draft never mentioned — email building-selection, the calendar write payload, AppFolio credentials, the platform-wide send switches, the read-side permissions — each now its own step. And the estimate roughly doubled, from fifty-three-to-ninety-three engineer-days to a hundred-to-a-hundred-sixty-four, once the review priced what the draft had left out. That doubling is the review working, not the plan failing.

The decisions on the table

None of these are taken. They are laid out as multiple choice, recommendation first, and turning any of it on at a real customer stays Fede's call. What does “company” mean — the recommendation is the operator, the account holder whose Clara answers, with owner and manager as relationship rows. What happens to Yale 25 — leave it under JP and Co, add the “owned by” and “managed by” rows, and change who runs it only if ConAm ever becomes the account holder; nothing moves today. Whether to ship the group tier now — recommendation is yes, ship the support, create zero groups. How taught answers attribute, how default grants work, how scattered homes are modeled — each has a recommended option and two alternatives.

The one with a real external dependency is Situs's calendars, because production refuses Google today. The options are: ask Situs to connect a Microsoft calendar, or finish the half-built Google writer that is sitting behind a guard. The recommendation is to ask Situs first, but price the Google writer in the same week so the answer doesn't wait on us. All of these are mirrored on the onboarding plan as decisions X10 through X17.

Proof: the harness

Fede's line was “if you're gonna recommend one architecture, I want proof,” so the recommendation ends where it should — with a test rig, not a conclusion. There is already a portfolio harness merged to main: a seeded company of eight scattered homes on one line, a second company sharing phone digits, twenty-two cases that are red on today's code. This extends it — same seed writer, same verdict contract, same fixture manifest, so the hash lock and the no-real-names guard cover every new case. Ten seeded portfolios, each modeled on a real shape but with invented names and numbers: the single building, the centralized company, the scattered-site manager, the owner-with-a-third-party-manager, the hybrid, the same human on staff at two customers, four hundred scattered homes, an acquisition, a joint venture.

And every headline assertion has the same shape: it must fail on today's code and pass after the step that claims it. Re-bind a line from a building to its company — new rows plus one claim rewrite, zero rows change partition, zero schema fields added. Camellia replays byte-identical before and after every step, and the shared agent's prompt-and-tool-schema hash is unchanged. An owner's login reads exactly its granted projection and zero other rows, with the negative case as a red test. An unmapped number is refused with zero writes and nobody minted. That is what “never migrate again” looks like when you make it something a machine checks, instead of something a document promises.

Organization architecture — one model for every operating shape

Organization architecture · Organization Core. Names updated September 16 to match the existing ORG# boundary. “Portfolio” means a collection of properties. Earlier recordings and quotations retain their wording; existing links and technical IDs remain stable.

How PropFlow models companies, buildings, owners, managers, people, phone lines, mailboxes, calendars and policies so that a single building, a centralized company, a hybrid, a third-party manager and a floating assistant are all the same system with different rows. Recommendation, evidence, an adversarial review, and the decisions the team has to make.

2026-09-06 · Fable, for the team's architecture discussion · revised after the adversarial review and the Fede/Gera review call (v2, evening) · status: proposed, no decisions taken yet

Recommendation. Keep the company as the customer and the wall between customers. Give every building exactly one company that runs it, optionally through a named group. Turn phone lines, mailboxes, calendars, the PMS login, policies and staff assignments into attachments on a node in that chain, with one function that walks the chain and refuses when it cannot name the company. Add dated relationships for who owns and who manages a building, and derive every login's view from those relationships. "Centralized" and "single building" stop being modes: they are where the phone line is attached.

The bar, as tests

Fede, Sep 6: "never have to migrate again"; the pattern has to cover single properties, multiple properties, centralized, decentralized, hybrid, people who rotate, people who are assigned, people who are centralized, one or many calendars, one or many lines; "scale from the current setup"; "I want proof." Written as five tests the design and its harness must pass:

  1. Never migrate again. Onboarding a customer of a new shape is data entry. Adding a building, or moving its line from the building to the company, adds rows and rewrites one claim; no partition changes and no schema field is added.
  2. Reachable from today. Every step ships dark and is a provable no-op for existing customers; Camellia is replayed byte-identical after each step, by a named instrument, not a sentence.
  3. Isolation is structural where it can be, and named where it cannot. The company is resolved once, at the edge, from the address the message arrived on, and unknown fails closed with zero reads. Thread selection, tool arguments and email building-selection are rules a resolver enforces, and the page says so.
  4. No modes. One voice-agent definition. No customer-type flag, no branch per shape. The property record's existing operatingMode field is retired.
  5. Views from relationships. A company that owns sees what it owns; a company that manages sees what it manages; the team at a building sees that building. Same rows, three views.

The model in one picture

Single building (Camellia) Company Building phone line mailbox · calendar line attached to the building caller: building known at pickup Centralized (Western Slope, Situs) Company phone line mailbox · PMS homehomehomehome…18 line attached to the company caller: building unknown, Clara asks Hybrid (the LA operator) Company Group shared line own lineown line some lines on buildings, one on a group no third mode: three rows, three places
Figure 1. Three real customers on one model. Boxes are the chain Clara walks (company, optional group, building). The highlighted chip is the phone line. The only difference between the three customers is which box the line is attached to. Mailboxes, calendars, the PMS login, policies and staff attach the same way.

Who runs it, who owns it, who sees it

JP&Coa company ConAma company Yale 25 Stationa building Camelliaa building runs · owns runs · owns manages (contract) What each login sees Sean, at JP&CoCamellia and Yale 25 — JP&Co owns both ConAm's teamYale 25 only — the building it manages Camellia's on-site teamCamellia only — their staff assignment Solid arrows: the chain Clara walks. Dashed arrow: never changes what Clara says; it grants a view. Nothing moves when ConAm becomes a customer of its own.
Figure 2. A building has exactly one company that runs it (the chain Clara walks and the identity pool residents belong to). It can also be owned by a company and managed by another. Those are dated relationship rows. Every login's view is derived from them, deny by default: an owner gets reports; a manager gets the operational view of the buildings it manages; a staff member gets their assigned buildings. Yale 25 stays exactly where it is today.

This is the part the morning's proposals and all five panel designs missed. They put the owner inside the operator's records as a label, which answers "group my buildings by owner" but not Fede's question: a company logged into its own account seeing a granted slice of a building that another company runs. Western Slope manages buildings it does not own, Situs owns buildings that Western Slope manages, some owners have a management arm, and ConAm needs to open Yale 25 without seeing Camellia. One dated relationship row per fact covers all of those without moving anything. The view a related company gets is computed from the live relationship at read time, never stored, so it ends when the relationship ends. Default grants: a manager gets the operational view of the buildings it manages and nothing else; an owner gets reports; anything more is explicit.

What this depends on in the code, stated plainly: today the dashboard decides what a login sees from a list on the login record, not from staff roles, and a company admin still sees every building on the platform because the July decision to fix that has not shipped. Those two things are the first half of the "views" step below, and no customer's people get a login before they are done.

A call on a company line

Someone dialsthe 970 number Whose number?one claim, one node Company: Western Slopebuilding: not yet known Walk up for valueshours · policies · calendar Same Claratold: ask which home Home named → checked: is it inside this company?yes → tours, applications, work orders run as today Camellia's line: same steps, building known at step threethe question is never asked; nothing about today's call changes One agent definition. "Ask which home" is a block of text injected as data when the building is unknown, not a second agent and not a branch in code.
Figure 3. The voice decision Fede already made, expressed on the model. The number's claim names the node. If the node is a building, the call is Camellia's today. If it is a company or a group, the building is a modeled "unknown" state, the same Clara asks, and a named home is validated to be inside the company before any tool runs. Today the voice tools trust a model-filled property id outright and an unmapped number adopts the caller's own building; both go away.

Two corrections from the review are now part of the picture. First, the voice platform will not accept a tool parameter that is both stamped by the server and typed by the model, so the building is not "demoted" to a proposal: the bound tools gain an optional, described "which property" text field the model fills from the conversation, and the server resolves it inside the company or refuses. Second, the check lives where the tools actually read their arguments, on both voice routes and in the leasing tools' own resolver, and the resolved company travels with the request as a required argument so a tool with no company refuses with zero reads. Email gets the same unknown-building state: a company mailbox message the company's roster cannot narrow asks or escalates; it never scores every building on the platform.

People and calendars

Situs: centralized, three leasing people take turns Company leasing person 1leasing person 2leasing person 3 own work calendarown work calendarown work calendar assigned at the company, covering all 14 buildings tours rotate across the three calendars (a setting, not code) Western Slope may use one company calendar instead: one row at the company node. Yale 25: a floating assistant, three calendars assistant her own calendar Yale 25building calendar another buildingbuilding calendar assignedassigned manager's calendar availability: her calendar first, then the building's, then the manager's
Figure 4. A person's calendar belongs to the person, never to the building; the production schemas of Cal.com and Deputy both do it this way, and so does multi-site healthcare scheduling. Assignment is a row: at one building, at several, or at the company. Rotation across calendars is a setting on the tour key. The same human working at two different customers is two person records, one per company, with an admin-only link and calendars that point at the same real calendar.

Per the review call, assignment across those calendars is deterministic: a schedule lives at the company or the building, code picks the host, and the model only calls "schedule a tour." One rule the adversarial review added: a calendar reachable from more than one company (the floating assistant's, when she works at two customers) receives busy blocks only. Today PropFlow writes the prospect's name, phone, email, stated income and Clara's notes into the calendar event, and the other company's connection would fetch those fields. The detail stays in PropFlow and the event links to it. And one prerequisite it surfaced: production refuses Google calendars today, the adapter is half built and a guard blocks it on every availability path. Situs runs on Google Workspace. Decision X17 below asks whether Situs connects a Microsoft calendar or we finish the Google writer.

What the Sep 6 review call added

Fede and Gera walked the recommendation together on the evening of Sep 6 (the raw transcript is in the standup thread). They converged on the same shape in their own words: a graph of nodes with capabilities that attach at any level, one resolver that knows how to walk it, and "a map at the top that tells you where each thing lives, so if you want to change anything it is done once, and you don't migrate." Points from the call that sharpen this page:

Every shape, as rows

Operating shapeHow it is expressed (data only)
Single building, own line and calendar (Camellia)Line, mailbox, calendar attached to the building. Zero groups. Byte-identical to today.
Centralized company, one number and mailbox (Western Slope, Situs)Line and mailbox attached to the company. Calendars: one per leasing person with rotation, or one at the company. Both are rows once the calendar step lands.
Hybrid (the LA operator Sean saw)Some buildings with their own line; a group holding the shared line for the cluster.
People assigned to one buildingA staff role at that building, and the dashboard reads roles.
People rotating across buildingsA staff role at the company or a group; their own calendar; rotation on the tour setting.
People centralized (a routing team)Same, plus the routing team's role at the company.
Floating assistant, two buildings, three calendars (Yale 25)Roles at both buildings; her calendar on her; building calendar on the building; manager's calendar on the manager. Person first, then place.
Third-party manager reporting by owner (ConAm)"Owned by" rows to each owner company; the owner's login gets a reports view; reports group by owner.
Owner-operator (Situs)"Runs" and "owned by" both point at itself. Nothing special.
Owner with a management armOne company; "managed by" rows from other owners' buildings pointing at it, with the operational view granted.
Manager that owns nothing (Western Slope)"Runs" for every building; "owned by" rows to the owners, Situs among them. An individual owner is a company record with one person.
Yale 25 todayRuns: JP&Co. Owned by: JP&Co. Managed by: ConAm, with the operational view of Yale only. Nothing moves.
Two companies owned by the same people (Situs, Western Slope)Two companies, two identity pools, relationship rows for the shared owners.
Joint venture on one buildingTwo "owned by" rows with shares; one "runs". Two operators on one building is refused; the honest model for that is two building records over one address.
AcquisitionDefault: keep both companies and give the acquirer a view through relationships. A true merge re-stamps the company on every row of the acquired company and reconciles two identity pools; it is a supervised, scripted job, not a re-parent.
400 scattered homes400 buildings (not 400 units of one property), four attachments at the company, groups for towns, a search tool above the size a menu can carry. Needs the dedicated work-order index first: today's lookup throws above 100 buildings per company.
A customer leavesClaims released, relationships ended (so derived views end), the company's rows retained or exported under a stated retention rule, its people and conversations never visible to the next holder of the number.

What it deletes

The hand-typed phone map and its inverted copy with two hand-pinned overrides; the fallback that texts a customer from another customer's number; the five files that must be edited in lockstep to add a phone number; the property record's unused operatingMode field and its switch; six company-settings fields that nothing reads; the voice path that adopts the caller's own building when a number is unmapped; the email match that takes the first building on the platform whose sender matches; the AppFolio credential lookup that falls back to the requesting user's own login; the never-expiring cache that assumes a building never changes company; the login-record list of assigned buildings (staff roles become the source); the calendar token on the property row (it moves onto the calendar attachment, so one shared calendar stops invalidating its siblings); the umbrella-property workaround and its six documented hacks; six of the seven ways settings are resolved today.

Getting there from today, one dark step at a time

012345678 company rows,required idaddress claimsin shadowresolver onone key, provenflip inboundper buildingcross-companyburn-downconversationbirth, front doorunknown buildingvoice + emailfinish read-sidepermissionsrelationships+ views this week, alongside the onboardings Western Slope's voice line needs 0–6; its texting also needs step 4 9101112131415 PMS loginper companysettings keys,with cleanupcalendars(decision X17)staff scope,groupsknowledgeby levelworkflowsnapshotsdeletions,offboarding In parallel from step 2: the operator screens (connections, settings by module, team, go-live checklist). Camellia is flipped last at every step.
Figure 5. Sixteen steps in two rows plus one parallel lane. Each ships dark, reads move before writes, old fields stay until the shadow window is clean, and each step names its rollback. Estimates are engineer-days and are estimates, derived from the file counts in the code inspection.
#StepProof and rollbackEffort (estimate)
0Company rows exist for every company id in use (today the self-serve path mints an id with no row), including on the test backend; one writer; the company id becomes required on every building; one property-creation factory (company, ticker, an explicit shadow stage, an escalation owner); the conversation record gains company and scope fields, read by nothing; the platform-wide settings route is restricted to platform admins and stamps who and when.Not a pure no-op: requiring the company id activates the existing fence on scoped reads, so this is verified against every real property before the flag. Rollback: the requirement flag.3–5 days
1Address claims and channel attachments written from today's fields and maps, read by nothing.A drift test compares the new lookup against the old one for every address in the five lockstep files: zero mismatches is the gate. Rollback: delete rows.4–7
2The resolver and the key registry with one key, the vendor roster, run in parallel with today's resolver against its 18 pinned assertions; the read budget (three queries, sixty-second cache) pinned by a test.Byte-equal on every real company and building. Rollback: a flag.5–8
3Inbound and outbound reads switch to claims, per building, behind a new switch keyed by node (the existing switch cannot name a company and already gates something else); the machine-sender suppression list is re-derived in the same change; caches get an expiry; an interim guard refuses any AppFolio login whose database differs from the building's. Writes stay on the old fields.Per building; Camellia last. Rollback: the switch, because writes have not moved.6–10
4The cross-company lookup burn-down: the drift guard's 20 open sites to zero, safe now because the company is known before each lane runs and the gas-leak page is re-anchored to the claim.The guard's count. Gates texting on Western Slope's line by the guard's own rule.5–10
5Conversation birth: the partition comes from a frozen birth anchor, never from the mutable building field; a company-line conversation is born in that company's front door, which is its own record type with one enumeration seam every per-building read goes through, plus an index by building; the cross-company thread fence reads the conversation's own company; a conversation carrying a company can never be stamped into the platform-wide "unrouted" pool.Harness: a company-line conversation appears on its building's page and in the rollups; exactly one record per conversation after a mid-call bind; zero new rows in the shared sentinels. Rollback: the anchor equals the building for every existing row, so old rows are untouched.8–12
6The unknown-building state for voice and email; the resolved company travels into the tools as a required argument; the model's building id is scrubbed to a checked value on both voice routes; the eight bound tools gain the "which property" text field (a live change to the shared agent's tool schema, with its own dark step and a prompt hash in the Camellia proof); email matchers require the company and ask instead of guessing; the voice fail-open path deleted.Camellia's building is always known; the new path is never taken; the prompt hash is unchanged.8–12
7Finish the read-side permission work accepted in July: company admins scoped to their company, the 21 unverified routes to zero, the dashboard reading staff roles instead of the login-record list.A gate, not a feature: no customer's person gets a login before the count is zero. Today any company admin can read any building's conversations by query string.6–10
8Relationship rows ("runs" derived from the tree; Yale gets "owned by" and "managed by"), views derived from them as a third read axis composed as a union at the single permission composition point; the resolver takes a principal.Harness: an owner's login reads exactly its granted projection and zero other rows; its negative twin as a red case.5–8
9AppFolio credentials become an attachment on the company (today one login holds exactly one database, so a shared staff account cannot serve two companies), re-wrapped under the new key; a second browser runner (a purchase, Fede's go); the account id threaded through the writer so the browser stops being pinned to one database.Before Situs's first AppFolio write. Harness needs a real credential path; marked.5–9
10Settings keys move to the resolver one at a time, each with "absent" set to today's hardcoded default, after a sweep deletes stored per-building values that equal the default (the form today writes every default as an explicit override) and the form is changed to send only what changed; the five company-wide send switches become company keys with a deny-only platform floor; an explicit "live" written for every building before the capability stage learns to inherit; renewals wired to the stage.Each key a no-op by construction, proven by saving each fixture's settings page once through the real route and asserting a company value still wins.12–20, spread
11Calendars: the token moves onto the calendar attachment; the calendar id becomes data instead of "the mailbox's default"; the tour slot carries the assigned host; availability unions the person's, the building's and the manager's calendars; busy-only across companies. Plus the Google writer if X17 says build it.Harness: N consecutive tours land on N different hosts' calendars. Google writer unpriced until X17.6–10
12Staff roles may point at a building or a group, and the inbound staff fence compares the destination, not the person's home company; the group tier; person calendars with rotation modes.Zero group rows for today's customers.5–8
13Knowledge by level: a company-level knowledge record; structured facts inherit only for the fields where an inherited value is true of the building (audited one by one; the code already refuses a fleet default for one); free-form sections shown at both levels, never merged; the isolation test rewritten to assert the walk visits only the building's own ancestors, and kept pinned.See the honest limit on knowledge below.5–8
14Long-running workflows snapshot the resolved scope at start and re-resolve only at declared checkpoints; a test forbids workflows from calling the resolver directly.Harness: a workflow started before the step completes after it with the same outcome.2–4
15Deletions; per-company ticker uniqueness together with the company-scoped work-order lookup on a dedicated index (never separately, and required before any company passes 100 buildings); offboarding paths: claim release, relationship end dates, a departing-customer rule.Deletion list above.5–8
UIIn parallel from step 2: the connections screen ("who does this answer for?"), the settings page by module with a scope switcher and the always-visible "why is this the value, set by whom, when" line and lock icon, the team screen, and the go-live checklist that is the resolver rendered.A capability shown ready that the resolver would refuse is a failing test.10–15

Total estimate 100–164 engineer-days, up from the draft's 53–93 after the review priced what the draft had left out. Steps 0–2 (12–20 days) run alongside the three onboardings. Google calendar writer and the browser runner are outside the total.

What each customer gets, and when

Honest limits

Adversarial review

Method. Eight attackers, each with one lens, tried to break the draft: under-built for the three onboardings; customer 5, 10 and 50; leaks; migration and rollback realism; "is this five proposals stapled together"; voice-platform and DynamoDB feasibility; the operator's screens; exact fit to Fede's owner-versus-operator and Situs statements. They produced 79 findings. Each finding went to two independent verifiers instructed to refute it against the code and to default to "not material" when unsure. A finding survived only if both said it was real and material.

OutcomeCount
Findings raised79
Survived both verifiers20
Refuted by both20
Refuted by one, conceded by the other (judgment calls, recorded)39
Survivors from the "customer 50" lens, which tested thirteen future shapes against the entity model0

Four root causes behind the twenty.

What no lens examined, found by the completeness critique, and now on the page: the knowledge-isolation downgrade (above, verbatim as all three judges asked); a step, a rule and a proof for taught answers, including on a company-line conversation with no building; the resolver's read budget and the ring-path latency it must fit; offboarding, which was absent from every document; who set a value and when; the full count of per-building configuration rows the settings step must port (8 record types, about 150 fields, 33 setter scripts).

Claims corrected. The draft's "88 fan-outs" is now stated as "dozens, depending on how you count" because seven verifiers got seven counts; "five fail-opens deleted" and the deletion list now agree; "inserts only" became "inserts plus one claim rewrite"; "steps 0–2 touch no live path" was struck because requiring the company id activates the existing fence; the estimate roughly doubled.

The full review, the eight attack reports, the refuted list with reasons, and the five proposals and three judgments it drew on are in the working directory for this study.

Decisions to make

None taken yet (Fede, Sep 6 evening). For the team, multiple choice, recommendation first; turning any of it on at a customer stays Fede's call. Mirrored on the onboarding plan, section 12, as X10–X17. Two earlier plan decisions already cover part of this: X5 (the same human at two customers: two records plus an admin-only link, recommended) and X9 (what Clara learns may cross homes: company facts shared, building facts isolated, recommended).

#DecisionOptions
X10What "company" means(A) The operator, the account holder whose Clara answers; owner and manager are relationship rows — recommended · (B) the owner · (C) both privileged
X11Yale 25 now(A) Leave it under JP&Co; add "owned by JP&Co" and "managed by ConAm" with the operational view of Yale only; change who runs it only if ConAm becomes the account holder — recommended · (B) move it under a ConAm company now · (C) two records
X12Conversations on a company line(A) Born in the company's front door with a frozen anchor, gain the home as a field plus an index, never move — recommended · (B) move partitions when the home is named · (C) umbrella property
X13The group tier(A) Ship the scope variant and resolver support now, create zero groups — recommended · (B) defer the tier · (C) create groups for Situs at onboarding
X14Taught answers(A) A staff reply teaches the building it answered; on a company-line conversation with no building yet, it teaches the company; one click promotes or demotes — recommended · (B) teaches the level of the line it arrived on · (C) always the company
X15Default grants(A) "Managed by" grants the operational view of that building only; "owned by" grants reports only; anything more is explicit; grants are computed from the live relationship, never stored — recommended · (B) owners see everything · (C) per-relationship configuration from day one
X16Scattered homes(A) One building record per home, with the dedicated work-order index built first — recommended · (B) one umbrella property with homes as units
X17Situs's calendars (production refuses Google today)(A) Ask Situs to connect a Microsoft calendar for tours · (B) finish the Google calendar writer (unpriced; it is the half-built adapter behind a guard) · Recommended: ask Situs first, and price B in the same week so the answer does not wait on us

Superseded or restated: the plan's "where a shared line binds the property" (its option A is this design); X6 owner grouping (superseded: an owner is a company with a relationship, not a group); X7 the portfolio test line purchase (unchanged, Fede's go).

Proof: the harness

"If you're gonna recommend one architecture, I want proof." The Sep 6 portfolio harness (a seeded company of eight scattered homes on one line, mailbox and calendar, a second company sharing phone digits, 22 cases red on today's code, merged to main on Sep 6) is extended, not replaced, using its seed writer, its case verdict contract and its fixture manifest so the hash lock and the no-real-names guard cover every new case. Ten seeded portfolios, each modeled on a named real shape, with invented names, numbers and addresses:

  1. A single building with its own line and calendar (Camellia's shape): the byte-identity control.
  2. A centralized company of 14 buildings, central office, three leasing people rotating with individual work calendars, an admin team, its own PMS database (Situs's shape).
  3. A scattered single-family manager, about 18 homes as 18 buildings, one number, one mailbox, one calendar, owning nothing, with owner companies including one that is itself a customer with its own PMS database whose ids overlap (Western Slope's shape).
  4. An owner company with one building it runs itself and one it owns but a third party manages, with the manager granted a view and a floating assistant with three calendars (JP&Co and ConAm's shape).
  5. A hybrid: a cluster sharing one line and mailbox through a group, two buildings with their own (the LA operator).
  6. A third-party manager with three owners, reporting by owner, with a per-building "what may be pooled" setting from the management agreement.
  7. The same human on staff at two customers.
  8. 400 scattered homes.
  9. An acquisition kept as two companies with a roll-up view; and a true merge as a red case for the supervised job.
  10. A joint venture: two owners with shares, one operator.

Headline assertions, each of which must fail on today's code and pass after the step that claims it:

Where this came from

Seven research streams, read in full: the team's Slack transcripts back to May (including the Situs whiteboard session, the Western Slope design-partner call, the ConAm onboarding threads, and every link Gera and Sean shared, among them Gera's Sep 1 audio overview); every internal document including Gera's Sep 1 "org tier as built" design and his "why folders break multi-tenant software" brief; a read-only inspection of the product code at the current main; outside research on multi-tenant hierarchies and settings inheritance (Google Cloud, AWS Organizations, Azure, Kubernetes, Slack Enterprise Grid, Stripe, Zendesk, GitHub, Auth0), on party, role and contact-mechanism data models (Fowler's Analysis Patterns read from the original text, Silverston, Hay, Cal.com's and Deputy's production schemas, hotel chains, multi-site healthcare), on how property-management and multi-location products model companies, owners, groups, lines and calendars (AppFolio, Yardi, RealPage, Entrata, Buildium, Propertyware, Rent Manager, Funnel, Knock, ShowMojo, RingCentral, Aircall), and 29 podcasts, talks and interviews.

Then five independent architects each designed from a different starting point (established data-model patterns; the code as it is; cloud resource hierarchies; Gera's designs taken to completion; the operator's screens), three judges scored them from three lenses (the engineer who ships it in four weeks, the architect at customer 50, the non-technical cofounder and the operator), and this recommendation was synthesized from all five, taking the skeleton from the cloud-hierarchy design, the step ladder and shadow-diff gate from the code-first design, the relationships from the data-pattern design, the grouping and claim mechanics from Gera's, and the settings screens from the operator-first one. Two gaps all five missed were added: the owner's cross-company view, and long-running workflows under a scope that can change. The adversarial round above then attacked the result, and this is the revised version.

Worth the team's time, ranked by the research

  1. Slack, "Changing the Model: Why and How We Re-Architected Slack" (QCon SF 2024). A company that bolted an org layer onto a single-workspace model and paid for years: about 300 settings resolved case by case. The cautionary tale for building the company tier now.
  2. "Mistakes People Make Building SaaS Software" (QCon London 2025). The bridge model between pooled and siloed tenancy, and why per-customer customization becomes debt.
  3. Funnel Leasing on renter-centric vs property-centric data. The clearest industry argument that the guest card belongs to the person, not the property.
  4. Multifamily Dive on centralization and maintenance pods. Leasing centralizes regardless of distance; maintenance does not.
  5. NAA on why centralization is harder for third-party managers. Management agreements pin staff per owner; pooling is a consent, not an org-chart fact.

Full research, proposals, judgments and attack reports are in the working directory for this study (seven research reports, five proposals, three judgments, eight attacks, the verified review); the harness lives in the product repo under the portfolio harness.

PropFlow Docs