Relationships
#1667 Finish the swamp_* attribute set on Attio users, and settle the two unprefixed ones (#809 follow-up)
Opened by keeb · 8/15/2026
Follow-up to #809. Separable from #1665 (which is about which operatives reach Attio); this one is about what we say about each of them, and applies at any scope.
#809 proposed six custom attributes and listed their final slug names as an open question, since a slug is a permanent contract once created:
swamp_username,swamp_role,swamp_tier,swamp_profile_url,swamp_signup_at,swamp_email_verified
What exists today
lib/infrastructure/attio-schema.ts declares, on users:
swamp_username— ✅ as proposedrole_in_collective— ⚠️ theswamp_roleidea, without theswamp_prefixemail_verified— ⚠️ theswamp_email_verifiedidea, without the prefix
And absent entirely: swamp_tier, swamp_profile_url, swamp_signup_at.
(On workspaces, swamp_slug is correctly prefixed; the rest of that object's custom attributes — plan, seats_in_use, trial_* — are likewise unprefixed.)
Why the prefix matters, and why this is not just tidying
#809's stated invariant was that the integration only ever creates swamp_* attributes, and the payoff was "a crisp, prefix-filterable blast radius — auditable, teardown-able, collision-proof". Two unprefixed attributes we created ourselves defeat exactly that: a human auditing the workspace cannot tell role_in_collective and email_verified apart from something Attio or a salesperson added.
The catch is that Attio cannot delete an attribute, only archive it, so a rename is not a rename — it is create-new + leave-the-old-one-populated-forever. That makes this a decision rather than a chore:
- Leave them. Accept two unprefixed attributes and correct the invariant's wording in the schema module and CLAUDE.md so it describes what is actually true.
- Add prefixed twins and migrate.
swamp_role/swamp_email_verifiedalongside, repaint via aCRM_MAPPING_VERSIONbump, and leave the originals archived and empty. Restores the clean blast radius at the cost of two dead attributes.
I lean (1) unless the audit story is load-bearing — the honest fix is to correct the claim rather than to churn a live workspace for it.
The three missing attributes
swamp_tier, swamp_profile_url and swamp_signup_at are additive, so they carry none of the above hazard. Notes before adding them:
swamp_tiermoves. #809's own design put a "mapping change" behind a versioned re-backfill for exactly this reason. A tier is a good CRM signal (it says how invested someone is) but it changes under you, and the existing projection deliberately excludes anything that moves daily — see the "usage attributes" note at the top oflib/app/attio-desired-state.ts, which parksswamp_score/last_seen/active_dayson the grounds that syncing raw movers is "a permanent write treadmill". Tier is coarse enough to be safe where score is not; quantize deliberately, do not sync XP.swamp_profile_urlis derivable from the username and costs nothing to keep current, but it duplicates a field that already moves when someone renames.swamp_signup_atis immutable and free — the clearest win of the three.
Adding any of them means bumping CRM_MAPPING_VERSION in lib/repositories/crm-records.ts, or the payload change is invisible in production while every test passes. Provision first with swamp model @keeb/attio/objects method run provision (dry-run by default) — scripts/attio-provision-input.ts derives the checklist from the schema module, so the attributes created and the payload sent cannot drift.
Open
No activity in this phase yet.
Sign in to post a ripple.