Skip to content

fix(platform-objects): the Setup Users list and the user record highlights stop showing the legacy role scalar - #28810

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-8n9g96
Nov 1, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-8n9g96

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

Requested by Leehom · project thread

Fixes #28105

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Setup → 用户列表(all_users 视图)去掉「Platform Role」一列;用户记录页顶部的高亮条去掉「Platform Role」一格。字段 sys_user.role 本身不动:仍是只读文本、仍在 Admin 分组、数据 API 照旧可读。
  • 为什么改:这一列是 better-auth 的遗留字段,平台从不写它;admin 插件开着时它给每个人都盖 user,种子平台管理员也是 user(本地实测见下)。平台管理员身份来自 ADR-0068 D2(所有者邮箱或无组织的 admin_full_access),不来自这一列。按裁决 D1(Ruling-ref: 6557746678)撤掉这个误导的展示面;真实身份推导的一列另立 platform-objects: the Setup Users list has no column that says who holds platform-admin standing, now that the legacy role scalar leaves it (#28105 D1 follow-up) #28793。
  • 风险与回滚:低。纯展示面改动,无类型、存储、选项或 spec 变化;已存的用户列表个性化视图不受影响(它们存自己的列)。回滚即撤销本 PR。
  • 先看:packages/platform-objects/src/pages/sys-user.page.ts(record:highlights 的 fields,高亮其实住在记录页,不在对象的 highlightFields);packages/platform-objects/src/identity/sys-user.object.ts(all_users.columns);packages/platform-objects/src/identity/sys-user-list-columns.test.ts。
  • 两分钟验证:pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 src/identity/sys-user-list-columns.test.ts src/pages/sys-user-record-highlights.test.ts,预期 8/8 通过(main 上 4 红)。
  • 需要你做的:无,席位落地。

What changes

  • all_users list view (sys-user.object.ts): columns are now name, email, phone_number, email_verified, banned, source, created_at. role is gone; banned stays (Setup → Users list has no Status (banned) or Role column, so a banned user or an admin cannot be spotted from the list (#27521 row 15) #27863). The column comment says why.
  • User record page highlights (pages/sys-user.page.ts): the record:highlights chip strip of sys_user_detail is now email, phone_number, email_verified. The details grid's hideFields keeps role (its comment now says why: not the platform role, rather than "already promoted to highlights"), and no details section names it, so the record page draws role nowhere. The page docblock no longer lists "platform role" among the signals.
  • Unchanged, per ruling D1: the sys_user.role field (Field.text, readonly, maxLength: 64, group Admin, label and description), its storage, the better-auth defaultRole stamp, and the ADR-0092 import / create-user pass-through. No synchronisation of platform-admin standing into the scalar. No packages/spec/** or ui/** edit.
  • Changeset .changeset/28105-sys-user-role-off-setup-screens.md: patch on @objectstack/platform-objects (served view and page metadata change; no authorable value is refused, no key moves).

Tests

  • Pin renamed and moved with the ruling. identity/sys-user-list-status-role-columns.test.ts (the Setup → Users list has no Status (banned) or Role column, so a banned user or an admin cannot be spotted from the list (#27521 row 15) #27863 pin, PR fix(platform-objects): Setup's user screens show a user's real state, and the copy-password line only when one was generated #28065) is now identity/sys-user-list-columns.test.ts, since its name named the Role column. It pins the new all_users columns, that no sys_user list view lists role, that the object highlightFields do not carry it, that every column is a declared field, and that role is still readonly text in group Admin with no options (ruling 2 guard).
  • New page pin pages/sys-user-record-highlights.test.ts: the header chip strip is exactly email, phone_number, email_verified; no record:highlights node anywhere on the page (tab children walked) carries role; no record:details section draws it and hideFields keeps it.
  • Red first, on base d9c4cdc4e (both files against base sources): 4 failed / 4 passed. Red: the all_users columns pin, "no list view shows role", the chip-strip pin, "no highlights node carries role". Green on base as well as head: object highlightFields (already name, email, email_verified on base), every column declared, the role-field guard, the details-section check.
  • Green on head: the two files 8/8; @objectstack/platform-objects full suite and typecheck green (numbers in Verification). The existing pages/sys-user-details-tab.test.ts pin of hideFields is unchanged and green.
  • No ablation: the change is declarative metadata, and the red-on-base run above is the pins' negative leg.

Acceptance notes

  • Mechanism (order assumption 1, confirmed). The record highlight showing role was not the object-level highlightFields (['name', 'email', 'email_verified'] on base) but the record:highlights node of the record page sys_user_detail. No compact layout exists (compactLayout is a retired key). The claim's file surface was amended to add the page file and its folder's tests.
  • Other readers of sys_user.role (order assumption 2). No other platform view, dashboard, report or example reads it as a column. The role columns in sys_member, sys_invitation, sys_scim_projection_grant and the related lists on the organization and user pages are membership / grant roles of other objects, not this scalar. Non-Setup readers left alone: the Console import panel's role column (ui/packages/app-shell/src/views/IdentityImportPanels.tsx, the ADR-0092 import pass-through), the expression-context user.role and its ${user.role === 'admin'} example in ui/packages/app-shell/src/providers/ExpressionProvider.tsx (the ui finding the seat carries), and packages/qa/dogfood/test/admin-ledger-decision-metadata.dogfood.test.ts, which reads the field label, not a list column.
  • Still reads "Platform Role". The field's label stays Platform Role (ruling 2: no field change), so the standard edit form and the data API keep that label. Noted, not changed.
  • Saved personal list views that copied role into their own column list keep it; this PR changes only the shipped all_users default.
  • No content/docs/** page pins the Users list columns (grep for the column list, all_users and Platform Role under content/docs: zero hits).

Verification

Live probe, stock showcase boot, base vs head

Commands (each leg on a fresh boot, platform-objects dist/ rebuilt for that leg; the base leg wrote the base blobs of the two source files over the head copies, then restored them with git checkout HEAD --, git diff HEAD empty and both blob hashes equal to HEAD before the head dist/ was rebuilt):

pnpm dev -- --fresh -p PORT
curl -c jar -H 'Origin: http://localhost:PORT' -H 'Content-Type: application/json' -X POST http://localhost:PORT/api/v1/auth/sign-in/email -d '{"email":"admin@objectos.ai","password":"admin123"}'
curl -b jar -H 'Origin: http://localhost:PORT' http://localhost:PORT/api/v1/meta/object/sys_user      # item.listViews.all_users.columns, item.highlightFields, item.fields.role
curl -b jar -H 'Origin: http://localhost:PORT' http://localhost:PORT/api/v1/meta/page/sys_user_detail # every record:highlights node's fields
curl -b jar -H 'Origin: http://localhost:PORT' 'http://localhost:PORT/api/v1/data/sys_user?fields=id,email,role&limit=2'
read base d9c4cdc4e (port 4625) head (port 4615)
sign-in 200 200
all_users.columns name, email, phone_number, email_verified, banned, role, source, created_at name, email, phone_number, email_verified, banned, source, created_at
object highlightFields name, email, email_verified name, email, email_verified
page record:highlights (header) email, phone_number, email_verified, role email, phone_number, email_verified
page record:highlights (Security tab) two_factor_enabled two_factor_enabled
fields.role text, readonly, group Admin text, readonly, group Admin, label Platform Role

Head data read: admin@objectos.ai (the showcase's seeded dev admin) answers "role": "user"; auditor.demo@example.com answers "role": null. That is the wrong value the ruling describes, and the field stays readable through the data API.

Gates, local subset, at head 280a84d (59 derived families, all exit 0)

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the real diff (merge base c064d3a2), each run with its exit code captured before any pipe, then reconciled: dispatch-gates --ran: 59 derived famil(ies) accounted for — 59 run, 0 NOT-MEASURED. check-plugin-teardown-shape --self-test first exited 3 on the shallow clone; after git fetch --depth=1 origin of its positive-control commit it passes 48 cases.

Also at this head: pnpm build (117/117 tasks), pnpm --filter @objectstack/platform-objects typecheck (exit 0), pnpm --filter @objectstack/platform-objects test (108 files, 1503 tests passed), pnpm exec eslint --no-inline-config packages/platform-objects (exit 0). test:repo and the full farm are CI's.

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-stale-fill :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

Generated by Claude Code

…rs list and the user record highlights

The all_users list view and the sys_user_detail highlight chips showed
better-auth's sys_user.role as the platform role. ObjectStack never writes
that scalar; platform-admin standing comes from ADR-0068 D2. The field
itself stays readonly text in its Admin group.

The #27863 list pin is renamed to sys-user-list-columns.test.ts and pins
the new columns, role's absence from every list view and the object
highlight fields, and the field's shape. A new page pin checks no
record:highlights node or details section draws role.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5msFrPy8g6WzAxVUjG
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects, touching 3 documentable anchor(s).

17 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 c9a9981dae1803fe5270c2171605c1c06508ee89.

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

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 42 pages)
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 101 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 2 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 c9a9981dae1803fe5270c2171605c1c06508ee89 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 8159540cb62a53289cda0ef52da2888aa84141d8 — the merge of head 280a84dd589999dcf8bb9155672cbc430e8c1189 into base c9a9981dae1803fe5270c2171605c1c06508ee89, 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 8159540cb62a53289cda0ef52da2888aa84141d8 && git checkout 8159540cb62a53289cda0ef52da2888aa84141d8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c9a9981dae1803fe5270c2171605c1c06508ee89 280a84dd589999dcf8bb9155672cbc430e8c1189 && git checkout -B drift-repro c9a9981dae1803fe5270c2171605c1c06508ee89 && git merge --no-ff 280a84dd589999dcf8bb9155672cbc430e8c1189

node scripts/docs-audit/affected-docs.mjs --json c9a9981dae1803fe5270c2171605c1c06508ee89

⚠️ 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 c9a9981dae1803fe5270c2171605c1c06508ee89 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

席位核对

ACCEPT · domain:server seat 5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt · 2026-11-01T21:21Z · head 280a84dd · #28105

The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.

席位意见:与 diff 相符,执行裁决 D1。Setup 用户列表去掉「Platform Role」一列,用户记录页顶部高亮条去掉这一格;sys_user.role 字段本身、存储、better-auth 的默认值写入都不动。真正按平台管理员身份推导的一列另立 #28793。

  1. Shape: draft against main, Fixes #28105, @objectstack/platform-objects patch changeset, Clause-②: no, Metadata-protocol: none. No contract review owed: no schema, rules or narrowing face (a served view and page change, nothing authorable refused). check-governed-merges --pr 28810: NOT governed, +151 / -44. git merge-tree against main: clean.
  2. Against the order: ruling 1 → role leaves all_users.columns and the record:highlights node of sys_user_detail (the dev measured that the chip strip lives on the page, not in the object highlightFields; claim amended, 6563206575); ruling 2 → the field is byte-unchanged (readonly Field.text, group Admin), no projection; ruling 3 → the Setup → Users list has no Status (banned) or Role column, so a banned user or an admin cannot be spotted from the list (#27521 row 15) #27863 pin is renamed to sys-user-list-columns.test.ts and pins the new columns plus role's absence, with a role-field guard; ruling 4 → no spec or ui edit.
  3. Accepted deviations: pages/sys-user.page.ts and a new pin beside it, added to the file surface by claim amendment. The details grid keeps role in hideFields, with its comment corrected.
  4. Gates: red first 4 of 8 on base, 8/8 on head; @objectstack/platform-objects 1503/1503; 59/59 derived gates exit 0. Check-runs on this head still running at ACCEPT; auto-merge waits on every required one.
  5. Out of scope: the follow-up column from real platform-admin standing is platform-objects: the Setup Users list has no column that says who holds platform-admin standing, now that the legacy role scalar leaves it (#28105 D1 follow-up) #28793 (blocked on this card, unblocked on merge). The ${user.role === 'admin'} example in ui/packages/app-shell/src/providers/ExpressionProvider.tsx that the ruling carries is a ui finding; not edited here.
  6. Landing: pr_ready + automerge_enable through the relay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

platform-objects: the Setup user screens show the raw stored platform role (user, admin) because sys_user.role is a text field

1 participant