Skip to content

Derive the org-role vocabulary from @objectstack/spec instead of mirroring it (blocked on the framework ADR-0108 release) #2908

Description

@os-zhuang

Follow-up to #2907, which corrected packages/auth/src/org-roles.ts but could not remove the mirror. This issue tracks the removal, and — more importantly — records the unblock condition, because it is not obvious from the code.

What is left

@object-ui/auth restates the four membership-role names that @objectstack/spec already owns:

export const ORG_ROLE_OWNER = 'owner';
export const ORG_ROLE_ADMIN = 'admin';
export const ORG_ROLE_DELEGATED_ADMIN = 'delegated_admin';
export const ORG_ROLE_MEMBER = 'member';
export const ORG_ROLES: readonly OrgRole[] = [ /* the four, in display order */ ];

The server-side equivalents are BUILTIN_MEMBERSHIP_ROLES and BUILTIN_MEMBERSHIP_ROLE_OPTIONS.

Why it could not be done in #2907

Three things have to be true, and today only the first is:

  1. The vocabulary is closed. Framework ADR-0108 (feat(auth)!: membership grade is not a capability channel — close the sys_member.role vocabulary (ADR-0108, #3723) objectstack#3802) retired app-declared organization roles, so the list is framework-owned and finite. Before that, deriving would have been deriving a list that should not have existed.
  2. @object-ui/auth depends on @objectstack/spec. It does not — its dependencies are @object-ui/types and better-auth. Adding it is the small part.
  3. The constants are published. They are not in any released version. @objectstack/spec@16.1.0 (latest on npm at the time of writing) was unpacked and its dist grepped: zero occurrences of BUILTIN_MEMBERSHIP_ROLES or MEMBERSHIP_ROLE_DELEGATED_ADMIN. They ship with the first release carrying ADR-0108.

Additional sequencing wrinkle: the packages that do depend on the spec pin ^16.0.0-rc.0, and the framework is in a v17 RC cycle. A caret range on 16.x will not pick up 17.x, so this needs the spec-version bump to happen first (or alongside).

The work, once unblocked

  1. Add @objectstack/spec to packages/auth/package.json at a version that carries ADR-0108.
  2. In org-roles.ts, turn the four export consts into re-exports and make ORG_ROLES [...BUILTIN_MEMBERSHIP_ROLES].
  3. Keep ORG_ROLE_LABELS and the grade ladder (orgRoleGrade, invitableOrgRoles, assignableOrgRoles) local. They are console concerns — i18n keys and screen-narrowing rules — and folding them into the name list would be the modeling error ADR-0108 D4 explicitly warns about: what names exist is a list; which names mean authority and how a name projects are rules that belong next to what they govern.
  4. Drop the drift-guard assertion added in docs(auth): the org-role vocabulary is closed — correct the mirror's standing instruction (framework ADR-0108) #2907 (is EXACTLY the framework four) — once the list is imported it cannot drift, and a test asserting a re-export against a literal is noise.

Why bother

This is the last of the copies ADR-0108 set out to collapse. The framework side already derives (the platform objects' two enforced selects and @objectstack/lint's tier set both read the spec constant), and deriving the lint's copy immediately exposed a real bug: the hand-kept list carried guest, a value the sys_member.role select has never offered, so an approver naming it resolved to nobody while the lint whose job is to catch exactly that stayed silent. Hand-kept mirrors of a server vocabulary do not stay correct; they only look correct.

Related: objectstack-ai/objectstack#3723 · objectstack-ai/objectstack#3802 (ADR-0108) · #2891 (the original consolidation) · #2907.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions