Skip to content

Rationalize the four overlapping "domain" columns #4159

@bokelley

Description

@bokelley

Context

We currently have four columns expressing different facets of "domain" with overlapping but not identical semantics:

Column Set by Read by
`organization_domains.is_primary` + row WorkOS webhook (auto-promote on first verified) + admin Set Primary auto-membership inference, seat-cap
`organizations.email_domain` WorkOS webhook + admin Set Primary `find_paying_org_for_domain`, brand hierarchy joins
`member_profiles.primary_brand_domain` brand-claim verify flow + (after PR #4157) WorkOS auto-populate publish-agent gate, brand registry resolve
`brands.domain` (+ `workos_organization_id`) brand-claim verify flow + WorkOS sync (`markBrandDomainVerified`) public brand catalog

Each has its own write path. Drift is real — see Media.net escalation #321 where members verified their WorkOS domain but couldn't publish agents because `member_profiles.primary_brand_domain` lived in a separate world.

Options to consider

(a) Keep both "primary" columns, single canonical write path. Org-membership-primary and brand-identity-primary are genuinely different concerns (one drives auto-link inference, one drives brand identity). Keep both columns but eliminate the multi-write surface — when a user takes any "set primary domain" action, all four sites update coherently.

(b) Collapse `member_profiles.primary_brand_domain` into a derived view. Replace the column with `SELECT domain FROM organization_domains WHERE workos_organization_id = $1 AND is_brand_primary = true` (new flag). Eliminates the drift surface entirely but is a more invasive migration.

(c) Audit consumer-by-consumer. Build a usage matrix of every code path that reads each of the four columns; collapse duplicated reads behind a single resolver function. Doesn't change the schema but reduces blast radius for future changes.

Why this matters

  • Members can't reason about which "primary" the UI is showing them.
  • Operators can't reason about which write path to use to fix drift.
  • We have a class of bugs (Media.net) that only exist because the two "primary" columns drift independently.

Driver

Media.net escalation #321. The full thread / staging plan: Stage 3 of the cleanup discussed there. PR #4157 is Stage 1 (auto-populate from WorkOS), #4158 is Stage 2 (member self-service UI).

Decision needed

Which option (or hybrid). Open question for design — not picking one yet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    admin-toolInternal tools for AAO staffclaude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.member-toolsAll member tools - profiles, dashboards, content management, working groups, events

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions