Skip to content

The back-compat capability derivation refreshes label/description on ANY existing sys_capability row, including admin-authored ones — the comment above it says the opposite #5876

Closed

Description

@baozhoutao

Found while implementing #4967 Part 1 (PR #5875). Out of scope there, filed for triage.

What

bootstrapSystemCapabilities' seed loop updates label/description on whatever row it
finds for a derived name, without looking at managed_by:

packages/plugins/plugin-security/src/bootstrap-system-capabilities.ts (~100):

for (const def of byName.values()) {
  const existing = await tryFind(ql, 'sys_capability', { name: def.name }, 1);
  if (existing[0]?.id) {
    // Keep label/description fresh, but do NOT clobber admin edits — only
    // platform-owned display fields are reconciled. `scope` is an
    // admin-editable classification face …, so it is seed-once …
    if (await tryUpdate(ql, 'sys_capability', { id: existing[0].id, label: def.label, description: def.description })) {

The comment claims admin edits are not clobbered. What #2909 T3 actually made seed-once is
scope, and only scope. For a derived name (as opposed to a curated platform one)
def.label is humanize(name) and def.description is Capability name. — so an
existing row's authored label/description are overwritten with a humanized placeholder
on every boot, whoever authored them.

Measured

PR #5875 added a pin that exercises exactly this shape (it is there to justify a design
choice in that PR, not to cover this defect):

packages/plugins/plugin-security/src/bootstrap-declared-capabilities.test.ts,
REVERSE: dropping that name from the list lets the derivation overwrite the admin row —
a row { name: 'showcase.export_data', label: 'Admin Made', managed_by: 'admin' } comes
out of the derivation pass labelled Showcase Export Data.

Reachability

Narrow but real, and it needs the admin row to pre-exist the grant:

  1. an admin creates capability X in Setup (managed_by:'admin' — the only provenance the
    ADR write-guard leaves admin-writable);
  2. an app whose bootstrap permission set grants X via systemPermissions[] is installed;
  3. every boot from then on rewrites that row's label/description to humanized text.

The reverse order is NOT reachable: once the derivation has created the managed_by:'platform'
placeholder, the package/platform write-guard blocks the admin from editing it at all, so
the more obvious "admin renames a derived capability" path fails earlier and differently.

Worth deciding as part of the same question: whether the derived pass should reconcile
display fields on a row it did not create at all, or only on rows it owns
(managed_by:'platform' created by itself) — the curated half of the loop has a real reason
to refresh (the platform's own definitions ship new copy), the derived half does not.

Not touched by #5875, which only stops refused declarations from suppressing the
derivation; the clobber above is on a different path and predates it.

Activity

  1. claude commented on Aug 6, 2026

    @claude
    Contributor

    分诊:pm:queue + domain:identity

    落点锚定:修复落在 packages/plugins/plugin-security/src/bootstrap-system-capabilities.ts 的 seed 循环(正文点名的 ~100 行 tryUpdate),plugin-security 属 identity 车道 ⇒ domain:identity。⛔ 未按标题里的 "capability" 词汇猜 domain:spec/engine-core —— sys_capability 的声明面确在别处,但这条缺陷的写入方只有这一个文件,单文件收口。

    判 pm:queue 而非 finding,三条依据:

    1. 落点与复现都已具名,且立单者已给出现成的验收挂点 —— PR fix(plugin-security): 被拒收的 capability 声明不再压掉派生占位,拒收诊断点名授权者 (#4967 Part 1/3) #5875 新增的 bootstrap-declared-capabilities.test.ts 里 REVERSE: dropping that name from the list lets the derivation overwrite the admin row 恰好走的就是这个形状({name:'showcase.export_data', label:'Admin Made', managed_by:'admin'} 被改写成 Showcase Export Data)。修法只需在 tryUpdate 前读 managed_by,不是设计题;
    2. 注释与行为相反,这一条本身就值得修 —— 代码里写着 "do NOT clobber admin edits",实际每次启动都 clobber。Audit sibling declared-metadata↔record two-store types (sys_position, sys_sharing_rule, sys_capability) per ADR-0094 addendum #2909 T3 做成 seed-once 的只有 scope。留着它,下一个读这段代码的人会据注释做出错误推断;
    3. 后果是静默数据丢失(管理员写的 label/description 每次 boot 被 humanize 占位符覆盖),不是外观问题。可达链虽窄(需 admin 行先于 grant 存在),但立单者已核实其反向不可达,即窄归窄、不是不可达。

    查重:三仓 open issue/PR 搜 sys_capability / managed_by / bootstrapSystemCapabilities,唯一相邻的 #2776(Permission Model v2 parking lot)带 tracking+status:parked,按分诊范围排除且非同题 ⇒ 无重复,不设 Blocked-by:。与 PR #5875 是不同代码路径(#5875 只管「被拒声明不再压制派生」),立单者已声明,核对属实。

    过时前提检查:git log origin/main -- packages/plugins/plugin-security/src/bootstrap-system-capabilities.ts 最近一次触碰仍在 #5875 之前,正文引用的代码形状在 main 上现存,前提成立。

    ⚠️ 给执行座位的一条范围提示:正文末尾那个「派生 pass 是否根本不该碰它没创建的行」是比缺陷本身更宽的一问。建议先落窄修(按 managed_by 守卫,消除 clobber),若实现中认为该收窄成「只 reconcile 自己创建的 managed_by:'platform' 行」,那属于策略变更 —— 请在 PR 正文说明,或另起决策件,⛔ 不要在同一 PR 里静默扩面。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 6, 2026
  3. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    ContributorAuthor

    认领:PM 循环第 5 轮(domain:identity 车道)
    会话:session_01JwwiU9bjhwy2SWj13ho8uv
    分支:claude/issue-5876-derived-pass-managed-by-guard
    Worktree:objectstack-issue-5876
    域:domain:identity
    文件面:packages/plugins/plugin-security/src/bootstrap-system-capabilities.ts + 测试 + .changeset/*

    按分诊指引窄修派发(tryUpdate 前按 managed_by 守卫,消除对 admin/authored 行的每 boot clobber);「派生 pass 是否根本不该碰它没创建的行」的宽问按分诊要求不静默扩面。验收挂点用 PR #5875 留下的 REVERSE pin(改红为绿)。同日 churn 仅 #5875 自身(已合并),基于新 main 实施。


    Generated by Claude Code

  4. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    ContributorAuthor

    复核 ACCEPT(PM,session_01JwwiU9bjhwy2SWj13ho8uv,第 5 轮):交付 PR #5934。

    CI 全绿(21 checks,FAILED: none),转 ready 入合并队列。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions