feat(approvals)!: rename role approver type to org_membership_level — and unbreak main#3137
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
xuyushun441-sys
force-pushed
the
feat/approver-type-org-membership-level
branch
from
July 17, 2026 13:01
0cb5a54 to
0abc1b3
Compare
Contributor
📓 Docs Drift CheckThis PR changes 3 package(s): 102 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…l` (#3133) `ApproverType.role` was the last platform surface projecting the word ADR-0090 D3 reserved. Renaming it also unbreaks `lint.yml` on main: the role-word ratchet has been red since #3113 (approvals.mdx 5 → 9), and the docs stop needing the word once the type is spelled correctly. D3's exception does not cover this enum. It protects better-auth's own `sys_member.role` COLUMN — third-party schema we cannot rename. `ApproverType` is ours: an authoring surface, i.e. the *projection*, which D3 says is spelled `org_membership_level` and labelled "organization membership", never "role". The sentence licensing the leak is itself false. ADR-0090 D3:203 claims `sys_member.role` is "already relabelled `org_membership_level` in the platform projection (ADR-0057 D7)" — but `org_membership_level` appeared nowhere in the codebase (one comment in position.zod.ts), and ADR-0057 D7:335 lists that relabel under "Deferred (evidence-gated, P4)". The projection never landed, so the word reached authors. The name manufactured a silent failure ("hotcrm class"): every sibling surface renamed to `position` (`sys_role`, `ShareRecipientType.role`, `ctx.roles[]`), so `{ type: 'role', value: 'sales_manager' }` reads as a position's legacy spelling. It resolves against the membership tier, finds no member row, falls back to an inert `role:sales_manager` literal, and the request waits forever. Repo-wide, `type: 'role'` had ZERO real callers — only lint tests and the docs warning that exists to undo the confusion the name creates. - spec: `ApproverType` gains `org_membership_level`; `role` kept as a deprecated alias for one window so a published 15.x flow keeps loading. `DEPRECATED_APPROVER_TYPES` + `canonicalApproverType()` are the single source for the mapping (runtime and lint both read it). - plugin-approvals: resolves on the canonical type, warns on the deprecated spelling, `expandRoleUsers` → `expandMembershipTierUsers`. The `type:value` fallback literal deliberately keeps the AUTHORED spelling — 15.x wrote `role:<v>` into `sys_approval_approver` / `pending_approvers`, and canonicalising it here would orphan every stored slot. - lint: `approval-role-not-membership-tier` → `approval-approver-not-membership-tier` (the rule id carried the word too), plus `approval-approver-type-deprecated`. Mutually exclusive: a bad VALUE wins, because prescribing `org_membership_level` for a position name is wrong advice — the fix there is `position`. - docs/skill/reference + role-word baseline ratcheted DOWN (approvals.mdx 5 → 1, automation SKILL.md 3 → 1); api-surface snapshot regenerated (0 breaking, 2 added). Studio still offers "Role" and its picker calls `client.list('role')` on a metadata type D3 deleted — that picker is already dead, and the dropdown is objectui's own hardcoded copy of this enum. Tracked as objectui follow-up in degrades to free text (strictly better than a picker that lists nothing). Closes #3133. Supersedes #3120 and #3122, which only deleted the duplicated docs callout without touching the name that causes the confusion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ated (#3133) Dogfooding the Studio flow designer surfaced the gap the rename alone left: the approver-type dropdown derives straight from this enum, so adding `org_membership_level` beside `role` put the NEW name in the list without removing the trap — `role` was still one click away from `position`. `.meta({ xEnumDeprecated })` on the approver type (sourced from DEPRECATED_APPROVER_TYPES, the single map) rides into the published node configSchema. Renderers omit listed members from pickers while still rendering a stored value, so a 15.x flow keeps loading. Locked by a test asserting the published schema carries it (the objectui side reads exactly this key). No new export (api-surface unchanged); generated references stay in sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s org_membership_level The generated reference's nested `approvers[].type` inline shape lagged the top-level enum. Regenerated (pnpm gen:schema && gen:docs) on latest main so the `check:docs` drift gate passes; xEnumDeprecated is a machine-readable hint and does not surface in the human reference. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
xuyushun441-sys
force-pushed
the
feat/approver-type-org-membership-level
branch
from
July 17, 2026 15:27
8636492 to
7d4fb1e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3133. Supersedes #3120 and #3122 (both closed) — they only deleted the duplicated docs callout without touching the name that causes the confusion.
This also unbreaks
mainlint.ymlhas been red since #3113 (approvals.mdxrole-word count 5 → 9), so every open PR inherits it. Fixing the name makes the docs stop needing the word at all — the ratchet goes green as a side effect, and ratchets down (5 → 1) rather than freezing 4 more occurrences in the baseline.Why the D3 exception does not cover this enum
ADR-0090 D3 reserves "role", with one exception:
The exception protects better-auth's column, which we cannot rename.
ApproverTypeis ours — an authoring surface an author types into a flow node, i.e. the projection. D3 says the projection is spelledorg_membership_level.And the sentence licensing the leak is false:
org_membership_levelappeared nowhere in the codebase — one comment inposition.zod.ts:250057-…md:335)The projection never landed, so the word leaked from the third-party column onto our authoring surface. Every sibling already converged —
sys_role→sys_position,ShareRecipientType.role→position,ctx.roles[]→positions— leavingApproverType.rolethe sole hold-out.The name manufactures a silent failure
Because everything else became
position,{ type: 'role', value: 'sales_manager' }reads as a position's legacy spelling. It resolves against the membership tier, finds no member row, falls back to an inertrole:sales_managerliteral, and the request waits forever on an approver that cannot exist. That is the "hotcrm class" incidentapproval-role-not-membership-tierwas written to catch — a lint rule existing to disambiguate a name is a smell pointing at the name (ADR-0077: make the mistake unwritable, don't lint it after the fact).Repo-wide,
type: 'role'had zero real callers — only lint tests and the docs warning that undoes the confusion the name creates.What changed
ApproverTypegainsorg_membership_level;rolekept as a deprecated alias for one window so a published 15.x flow keeps loading.DEPRECATED_APPROVER_TYPES+canonicalApproverType()are the single source for the mapping (runtime and lint read it), so removal next major is a one-line edit.expandRoleUsers→expandMembershipTierUsers.approval-role-not-membership-tier→approval-approver-not-membership-tier(the rule id carried the word too) + newapproval-approver-type-deprecated.Two decisions worth reviewing
role:<v>intosys_approval_approverandpending_approvers; canonicalising it here would orphan every stored slot. Locked by a test.{ type: 'role', value: 'sales_manager' }asorg_membership_levelwould be wrong advice — the fix isposition. Locked by a test.Verification
check:role-wordcheck:doc-authoring/check:release-notescheck:api-surfacespec exec tsc --noEmit/pnpm lintminor, not major: the enum only gains a value,rolestill parses and resolves, and the one removal (APPROVAL_ROLE_NOT_MEMBERSHIP_TIER) is a lint const with zero consumers incloud/objectui. All three packages are in the same 69-package fixed group. The actual removal ofroleis next major.Follow-ups (not in this PR)
client.list('role')— a metadata type D3 deleted, so that picker is already dead and actively walks authors into the trap. objectui hardcodes its own copy of this enum (flow-node-config.ts), so it needs its own PR. The xRef map here points both spellings at the new picker kind, which degrades to free text — strictly better than a picker that lists nothing.content/docs/references/has pre-existing drift from spec (7 unrelated files regenerate dirty, e.g.allowTransferstill says "pending M2" though 安全:owner_id(属主锚点)客户端可写、服务端无守卫 → 非属主可伪造/转移记录属主 #3004 enforced it). Deliberately reverted here to stay focused; nothing in CI catches it.v14.mdxstill names the old rule id — correct as history, left alone.🤖 Generated with Claude Code