Skip to content

org_membership_level approver sources a three-value tier list while sys_member.role enforces four (delegated_admin unreachable) #9806

Description

@os-elon

Found while implementing objectstack-ai/objectos#120 (documenting the sys_notification_subscription.principal selector, which resolves role: against sys_member.role). Out of that card's scope; filed unassigned. Source read only — nothing measured at runtime.

The two lists

packages/spec/src/automation/approval.zod.ts:139:

/** Org-membership tiers (`sys_member.role`: better-auth's closed set). */
export const ORG_MEMBERSHIP_LEVELS = ['owner', 'admin', 'member'] as const;

packages/spec/src/identity/membership-role.ts:99-104 — "the WHOLE vocabulary, in display order (ADR-0108)":

export const BUILTIN_MEMBERSHIP_ROLES = [
MEMBERSHIP_ROLE_OWNER,
MEMBERSHIP_ROLE_ADMIN,
MEMBERSHIP_ROLE_DELEGATED_ADMIN,
MEMBERSHIP_ROLE_MEMBER,
] as const;

sys_member.role declares options: [...BUILTIN_MEMBERSHIP_ROLE_OPTIONS] (packages/platform-objects/src/identity/sys-member.object.ts:208-214), and that file's own comment calls it "The framework's four roles — the WHOLE list", "ENFORCED on write". sys_invitation.role uses the same option list (sys-invitation.object.ts:224).

Why it matters

APPROVER_VALUE_BINDINGS.org_membership_level (approval.zod.ts:155) sources the approver picker from ORG_MEMBERSHIP_LEVELS, so a delegated_admin membership tier appears to be unaddressable as an approver even though it is a storable, enforced value of the column the approver type names.

The doc comment is also stale on its own terms: it attributes the list to better-auth, but delegated_admin is ObjectStack's own addition (ADR-0105 D8 / #3697), so "better-auth's closed set" is no longer the same set as sys_member.role's.

Two readings — a decision, not a patch

  1. Deliberate: delegated_admin must never be an approver tier. Then the constant needs a comment saying so and naming the reason, since today it reads as an out-of-date copy of the vocabulary.
  2. Drift: the picker should offer whatever BUILTIN_MEMBERSHIP_ROLES holds, and ORG_MEMBERSHIP_LEVELS should be derived from it rather than re-spelled, so the next tier addition cannot silently miss the approver surface.

Docs echo filed separately in ObjectOS (the published approvals page repeats the three-value list).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions