Skip to content

fix(platform-objects): retire the set_user_role action from sys_user - #11530

Merged
os-sam merged 2 commits into
mainfrom
claude/issue-9968-retire-set-user-role-probe-impersonate
Aug 24, 2026
Merged

fix(platform-objects): retire the set_user_role action from sys_user#11530
os-sam merged 2 commits into
mainfrom
claude/issue-9968-retire-set-user-role-probe-impersonate

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Part of #9968

Summary

This PR ships half 1 only — the set_user_role retirement. Half 2 (impersonate_user) is already fixed and merged on main: PR #10352 (merged 2026-08-21) re-implemented /admin/impersonate-user as a better-auth plugin endpoint and admits ObjectStack platform admins today. That half's premise ("still 403 every platform admin") is false on current main — see the Measurements section below. No impersonate-side code changes are in this diff.

set_user_role — retired, not re-implemented

sys_user's set_user_role action ("Set Platform Role") is removed. Its only effect was POST /api/v1/auth/admin/set-role, which better-auth's admin plugin lowers to internalAdapter.updateUser(userId, { role }) — a gated, UI-driven writer for the legacy sys_user.role scalar ADR-0068 D2 stopped synthesizing. Platform-admin membership is granted through sys_user_permission_set / admin_full_access; a working button was a supported, one-user-at-a-time channel for resurrecting the dual identity representation the 2026-08-18 ruling permanently vetoed (Option 3). Matches the maintainer's Option B ruling (2026-08-20, reaffirmed 2026-08-22).

What stays unchanged. The vendor's POST /api/v1/auth/admin/set-role route itself stays mounted and vendor-gated exactly as before — only the sys_user console action pointing at it is gone. Every other sys_user admin action (ban_user, unban_user, unlock_user, create_user, set_user_password, impersonate_user) is unaffected.

Files changed

  • packages/platform-objects/src/identity/sys-user.object.ts — removes the set_user_role action block; leaves a retirement note in its place.
  • packages/spec/src/kernel/public-auth-features.ts — drops the now-nonexistent 'sys_user.actions.set_user_role' entry from PUBLIC_AUTH_FEATURES.admin.gatedInputs (the completeness guard reads this bidirectionally; forcing the edit, not a discretionary spec change).
  • packages/platform-objects/src/platform-objects.test.ts — removes the two hard-coded references to the retired action, and adds a dedicated pin (#9968 — set_user_role is retired from sys_user, every sibling admin action survives) asserting the retired name is gone by name and every one of the 15 sibling actions survives by name, in one test so a retirement that removed the wrong entry (or several) cannot pass.
  • packages/platform-objects/src/feature-gate-guard.test.ts — the hand-maintained walker floor drops from 38 to 37 booked gated inputs (comment updated to say why).
  • packages/platform-objects/src/pages/sys-user.page.ts — two doc-comment mentions of the retired action removed for accuracy.
  • packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts — regenerated via node scripts/check-i18n-bundles.mjs --write (never hand-edited); drops the set_user_role label/successMessage/param strings.
  • .changeset/retire-set-user-role-action.md — patch changeset on @objectstack/platform-objects + @objectstack/spec.

Out of scope, deliberately untouched

  • The vendor POST /api/v1/auth/admin/set-role route stays mounted (unaffected) — auth-route-ledger.ts's BETTER_AUTH_MOUNTED_SURFACE, vendor-admin-refusal-envelope.ts/test, and the dogfood/checklist coverage that exercises that route directly (not via the sys_user action) are all still accurate and untouched.
  • docs/adr/0092-sys-user-profile-field-delegation.md mentions the retired action in a historical implementer note; docs/adr/** is a governed surface (Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14) and this PR does not touch it.

Measurements

Half 1 premise — CONFIRMED valid before this PR

git grep -n "set_user_role" origin/main -- packages/platform-objects packages/spec showed the action still declared on sys_user at packages/platform-objects/src/identity/sys-user.object.ts:253 before this change.

Half 2 premise — FALSE on current main (measured, not assumed)

Tests

  • pnpm --filter @objectstack/platform-objects test — 452/452 passed (28 files).
  • pnpm --filter @objectstack/platform-objects typecheck — clean.
  • pnpm --filter @objectstack/spec test — 11204/11204 passed (420 files).
  • pnpm --filter @objectstack/spec typecheck — clean.
  • node scripts/check-i18n-bundles.mjs — in sync after --write regeneration (8 bundles).
  • node scripts/pm/dispatch-gates.mjs (no paths) — full derived gate list run; all green, including the repo-wide check:type-check-debt ratchet re-measure (32 ledger entries, none above their recorded ceiling).

Generated by Claude Code

set_user_role's only effect was internalAdapter.updateUser(userId, { role })
- a gated, UI-driven writer for the legacy sys_user.role scalar ADR-0068 D2
stopped synthesizing. Platform-admin membership is granted through
sys_user_permission_set / admin_full_access; a working "Set Platform Role"
button was a supported, one-user-at-a-time channel for resurrecting the dual
identity representation the 2026-08-18 ruling permanently vetoed (Option 3).
Removal, not a narrowed re-implementation (maintainer ruling, 2026-08-20,
reaffirmed 2026-08-22, Option B). The vendor's POST /admin/set-role route
itself stays mounted and vendor-gated, unchanged - only the sys_user console
action pointing at it is gone.
packages/spec/src/kernel/public-auth-features.ts drops the corresponding
'sys_user.actions.set_user_role' entry from PUBLIC_AUTH_FEATURES.admin's
gatedInputs - the bidirectional feature-gate-guard completeness check in
platform-objects reads this registry and forces the edit.
Pinned in one test with its counter-direction so a retirement that removed
the wrong entry (or several) cannot pass: the retired name is asserted gone
BY NAME, and all 15 sibling sys_user actions are asserted present BY NAME.
Translations regenerated via `node scripts/check-i18n-bundles.mjs --write`
(never hand-edited).
#9968 half 1. Half 2 (impersonate_user) is already fixed and merged on main
via PR #10352 - no changes needed here; see the PR body for the measurement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/spec, touching 6 documentable anchor(s).

42 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json d10e2146263913d0f81b36607b41d21926249837.

5 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/platform-objects/src/pages/sys-user.page.ts) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json d10e2146263913d0f81b36607b41d21926249837packageMentionDocs.

Which tree this was computed on

This run read content/docs from 65edcb806f6d9726e9bcb5f62eacaac785f58c57 — the merge of head 4d5129541635718ba3482aa03e944af5f78057ac into base d10e2146263913d0f81b36607b41d21926249837, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 65edcb806f6d9726e9bcb5f62eacaac785f58c57 && git checkout 65edcb806f6d9726e9bcb5f62eacaac785f58c57
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d10e2146263913d0f81b36607b41d21926249837 4d5129541635718ba3482aa03e944af5f78057ac && git checkout -B drift-repro d10e2146263913d0f81b36607b41d21926249837 && git merge --no-ff 4d5129541635718ba3482aa03e944af5f78057ac
node scripts/docs-audit/affected-docs.mjs --json d10e2146263913d0f81b36607b41d21926249837

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs d10e2146263913d0f81b36607b41d21926249837 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 24, 2026
@os-sam
os-sam marked this pull request as ready for review August 24, 2026 03:07
@os-sam
os-sam added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit 033a34cAug 24, 2026
33 checks passed
@os-sam
os-sam deleted the claude/issue-9968-retire-set-user-role-probe-impersonate branch August 24, 2026 03:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-sam@claude