Skip to content

Hat Relationships

All hats across the org hierarchy, grouped by level. One account can hold multiple hats — expand a hat and toggle which other hats it can wear. Only hats strictly beneath a hat are offered (act-as is downward-only); the Poppy level (L0) is Super Admin only. Changes save to the database instantly.

Hats Level groups Live today Wear rules

Onboard a person

We never set anyone’s password, and we should not. An admin-chosen password has to be sent to them somehow, is known to two people from the moment it exists, and destroys the ability to prove an action was theirs — a compliance problem for a brokerage, not just a security preference. Instead: they get an invite, THEY choose the credential, and a second factor enrols on first sign-in. A reset is the same shape — a one-time link, never a password read out over the phone.

Give someone a hat

Hats compose — one login, many hats. Adding one here grants it immediately; the person sees it on their Identity page along with every surface it opens. Rank comes from the catalogue below, so a hat always means the same thing wherever it is granted. Every grant is written to the audit log with who did it.

Access

The hat catalog

A hat carries a tier (which organisation it belongs to) and a name (what that organisation calls it). Nothing said that MLS Admin, Membership Admin, Office Manager and Team Admin are the same job at four tiers — so a decision about what an administrator may do had to be made four times, and the four drifted. Rank is that missing axis. Read a row across: one job, its local names. The row is where shared attributes belong; the name stays freely editable per organisation.

Every hat says LIVE because pure_hats.status is live on all 54 rows — one value, set at seed time, checked by nothing on any schedule. It meant defined and was being read as in use. This column is derived at read time from evidence, so there is no schedule for it to fall behind, and the stored value is shown beside it so the gap stays visible instead of being quietly overwritten.

Who the hats belong to

PURE keeps two populations, not one roster, and that is deliberate. The directory (pure_org_members) is real association and brokerage people — board members, staff, volunteers — many with no email because they have no PURE login. The identities (pure_identity_hats) are the people who sign in and wear hats. A directory row with no hat is not a broken join; it is a person without an account.

Every person, and every hat they hold

One row per person across all three populations — the hats, the directory and the logins — so somebody who is in one and missing from the others shows up as a row rather than as nothing. Remove a hat with the × on its chip; give one with + hat. Both write immediately.
This page owns hats and logins, not person records. A person’s name, title, phone, licence and timezone are edited in the User Directory, which writes pure_org_members through mcp_person_upsert. Two surfaces list people today — one more than there should be — and which absorbs the other is an open decision. Saying so here beats leaving a reader to guess which page is authoritative.

Hat Relationships · pure_hats + pure_hat_wearable · PURE merged workspace

Access health & proposed audiences

Proposed audiences Admin menu Primary menu Accept all shown

Each proposal is the hat list the front rail’s own module registry already carries for that page, with shorthand resolved to catalog hats. Nothing here is written until you accept it, one row at a time or all of the rows currently shown.

Titles vs access hats
Set access hat

Setting an access hat never touches the membership title. Clearing it (choose “none”) leaves the person in the directory with no PURE permission of their own.

Recommended hats — title-only members Adopt all high-confidence shown

Every field on every surface, classified against the L0–L4 sensitivity scale. A row highlighted amber got its level from a default, not a review — changing it here records you as the reviewer. The verdict a page enforces still comes from the server (mcp_field_controls); this is where the classification behind that verdict is owned.

Organization levels per surface Adopt every suggestion

Suggested = the levels of the hats that can already open the page. A page open to everyone suggests every level, which is not a decision, so those are marked and skipped by the bulk adopt. Click a level chip to tag or untag — it writes immediately and is audited.

Organizations Complete list — Org model
Add organization

Adding is idempotent by name: an organization that already exists is returned rather than duplicated, so a second click cannot fork “Acquisto Real Estate” into two records. These are organizations, not the roster’s hat levels above.

Pages no menu reaches All Rail Link only Undecided
Spine access — do the hats and the page gates agree?

All hats — totals & reach

Reach = nav items this hat can see across the primary and admin menus: unrestricted items + items granting the hat (directly or via a declared alias). Click a hat to open its card above.

Add a hat

The new hat immediately joins every nav item already open to all hats of the chosen organization level (and every unrestricted item by construction). Partial selections are deliberate and are never touched.

Surfaces this hat can see Show restricted surfaces

Governance

Vocabulary — who can be on a deal, and what a vendor is

Two columns used to hold these as free text. transaction_parties.role carried 17 spellings for about 8 rolesbuyer agent beside buyer_agent, coop agent beside co-op agent (other side). pure_vendors.kind had four spellings of inspection and four of photography. Every spelling still works; it just resolves to one row now. Editing a row changes the Deal Room picker on the next searchmcp_party_search reads these rows rather than a list written into its own body.

A role says who someone is on the deal and what they are responsible for. Friend / Client, Referral contact and the divisions are parties too (Mike, 9/1) — they are mirrored from pure_party_roles, the CRM-side source, and carry their own duties. Licence class is what the picker searches: trec_agent, trec_broker, trec_inspector, vendor:<kind>, crm:<role>, or blank — and blank means the picker says it has nothing mapped rather than returning our own roster as if it were the answer.

pure_vendor_roles already existed and already carried synonyms — it is the vendor vocabulary, so nothing new was built here. Synonyms are the spellings found in pure_vendors.kind; anything listed as unclaimed below is a spelling no row owns yet, which is a real gap rather than a rendering problem.

A listing task is owned by one of four kinds of thing (Mike, 9/1) — and only one of them is a person on the deal. pure_listing_tasks.owner_kind says which kind; owner_role names the party, and is set only when the kind is party. That pairing is a database constraint, not a convention, so a task can never claim a system owner and carry a stale party role beside it. The four kinds are structural — code branches on them — so their wording and order are editable here and a fifth is a code change, not a row.