Skip to content

[Decision] 卸载包后岗位定义仍列着该包的权限集(重装后恢复生效):维持(A)、维持并在启动报告点名(B)、卸载时拦下(C),还是替作者改定义(D) #23229

Closed

Description

@objectstack-fleet

Ruled: 6145039294 · letter B · the reading confirmed; the sixth report class is an 8-post closeout item on #22975; this card closes · 2026-10-13T12:03Z
Part of #15204
Filing gate ②:只有维护者能做的决定。这是对维护者自己裁决用词的读法:6123311533 里的「岗位授权」是否也包括岗位定义。也算 ③:维护者在本席会话中要求「补这 4 张决策卡」。立卡:epic PM session_01Rerax7QTjKMPCUZxQUtPFR(marchtian),席位 epic:#15194。
Prior rulings on this card: none(新卡)。相关裁决:6123311533(维护者,方案 1:卸载按名称撤销授权)。审查方的升级:契约审查 6128268740 ①9 与 ③ 第 2 条。D 的维护者速读 6130417182「需要你确认的一处读法」。

锚点: 这个问题原本挂在 #22900(阶段 6b-2 D)的维护者速读里。D 已于 2026-10-13 02:19Z 合入,标签已摘,问题没有了归处。它跨越阶段 8 和 8-post(#23056、#22975),没有单一自然锚点,所以单开本卡。

查重: 用语义检索搜 objectstack 卡,关键词 uninstall / position definition / permissionSets / dangling reference / unserved set / boot report,命中 15 张,前 8 张全部已关(#21669、#21576、#21490、#22360、#22307、#22169、#22379、#21860),都不是这个问题。没有同题的开着的卡或决策卡。

动手的读者:

维护者速读

你裁过的(6123311533,方案 1): 卸载包时,按包的权限集名称撤销「用户或岗位授权」,因为留着的话,包不在时不生效、重装后又生效,违反 ADR-0090「不留幽灵授权」。D 已按此实现:用户授权行,以及旧的岗位绑定行,卸载时都撤销。

剩下的一种情况: 某个岗位的定义在 permissionSets 里列了被卸载包的权限集。定义可以是代码声明的,也可以是 Studio 里作者写的。按 ADR-0131 D3,岗位带哪些权限集是岗位定义的一部分,是作者的元数据,不是授权行。D 不改它,所以:

  • 包卸载后,这一项立刻不生效;
  • 包重新安装后,这一项恢复生效;
  • 启动时的目录引用报告不会提示这种悬空引用,这是静默的。

审查方的读法: 你说的「岗位授权」指授权行,不指岗位定义,这和 ADR-0131 D3、ADR-0090 原文一致。但「包不在时不生效、重装后恢复」正是你裁决里描述的形状,所以要你确认。

四个选项:

  • A: 同意读法,不动,本卡关闭。
  • B: 同意读法,并让启动报告新增一类「岗位定义列了目录里没有的权限集」,按岗位逐个列出。行为不变,只是不再静默。
  • C: 卸载时,如果有岗位定义引用该包的权限集,就拒绝卸载并列出这些岗位,作者先改定义再卸载。
  • D: 卸载时,替作者把这些名字从环境里作者写的岗位定义中删掉。代码声明的岗位是封存的,改不了,所以覆盖不全。

本席推荐 B。 选 A、B、C 还是 D?

背景(实测:objectstack origin/main 8024e1243a,2026-10-13T09:25Z)

Governing text

  • ADR-0131 D3: 「The position → permission-set binding is part of the position's definition」;「a definition lives only in the environment registry — code-declared (managed, sealed) or environment-authored (Studio, template package; editable by metadata-authoring capability holders)」。
  • ADR-0131 D6: 「Managed definitions are sealed; customization is environment-level state beside them, never an edit」。代码声明的岗位定义不能被改写。
  • ADR-0131 环境元数据的来源规则(维护者原话): 「代码推送过来的元数据就只能在代码中修改;studio 界面上配置的元数据,保存在库中,可以在界面上修改」。
  • ADR-0090(everyone 一节): 「uninstalling the package (removing its sets by packageId, ADR-0086 D3) revokes it everywhere at once. No ghost grants.」
    • 原文的机制是「拿掉权限集,所有引用随即失效」,没有提到重装。
  • 裁决 6123311533: 「A user or position grant naming the package's set would then survive the uninstall: inert while the package is absent, and live again on reinstall. That breaks ADR-0090 D5 ("no ghost grants"). Ruling: the cleanup revokes those grants by the package's catalog set names」。

前提(每条带复核命令)

  • 启动报告没有这一类:git show origin/main:packages/plugins/plugin-security/src/catalog-reference-report.ts | sed -n 1,60p | grep -c "permissionSets",应为 0。
  • D 已合入:git merge-base --is-ancestor c8a9783eca origin/main && echo merged。
  • 岗位定义带权限集是规范字段:git grep -c "permissionSets" origin/main -- packages/spec/src/identity/position.zod.ts,应 ≥1。
  • 阶段 8 还没合入:gh api repos/objectstack-ai/objectstack/pulls/23222 --jq .merged,应为 false。

选项 × 真实代价

选项 做什么 能感知的后果 工作量
A 维持,结案 不动 卸载后,引用该包权限集的岗位悄悄少了这部分权限,重装后悄悄恢复;没有任何提示 0
B 维持,报出来 启动报告加第 6 类:岗位定义里目录解析不到的权限集名,按岗位列出 行为同 A,但每次启动日志都点名「岗位 X 列了不存在的权限集 Y」;重装后自动从报告里消失 1 个 PR,在 catalog-reference-report.ts 上加一类加测试,规模估计百行级(未实测);先例是这个报告现有的 5 类;放进 8-post 一起做,避免和阶段 8 冲突
C 卸载时拦下 两个卸载入口先查岗位定义;有引用就拒绝卸载并列出岗位 运营者卸载包前必须先改岗位定义;不会出现「重装后悄悄恢复」;卸载多一个失败理由 1 个 PR,覆盖两个卸载入口加测试和消融,规模未实测,无先例;Clause-②: yes (narrowing)
D 替作者改定义 卸载时从环境里作者写的岗位定义中删掉这些名字 作者写的岗位定义被卸载动作悄悄改掉;代码声明的岗位改不了,问题还在 1 个 PR,要写环境元数据账本,规模未实测,无先例;和 D6 的封存冲突,只能覆盖一半

业务含义直译

  • A: 员工手册写着「财务岗可用 X 系统」。X 系统下线,手册不改;X 回来,手册照旧有效。没人提醒手册里有一条暂时作废。
  • B: 同 A,但每天早上的晨报会列出「手册第 N 条引用的 X 系统当前不存在」。
  • C: 想下线 X 系统,得先把手册里引用它的条目改掉,否则不准下线。
  • D: 下线 X 系统时,行政悄悄从别人写的手册里删掉相关条目;但印刷版手册(代码声明的)改不了。

四轴(从业务看)

  • 项目长远合理性(领起):
    • 两年后的样子:岗位定义是作者的,按名称引用权限集,引用不到时平台大声说出来,不替作者改。
    • ADR-0131 D4 和这份启动报告就是按这个思路建的:引用按名字,解析不到时 fail closed,并由报告负责「大声的那一半」。
    • 参照主流做法:
      • 编译器和链接器对未解析的符号报警告或错误,不替你删代码;
      • Salesforce 卸载托管包时,如果包组件被包外的元数据引用,会列出依赖、阻止卸载(相当于 C)。
    • B 和 C 都在这个方向上。A 留下一个静默的洞。D 让包写进别人的定义,和 D6 冲突。
  • 实际业务拉动: 今天没有用户报告撞上它。会撞上的是:运营者卸载一个包,而某个岗位定义列了这个包的权限集。持有这个岗位的人悄悄少了权限,重装后又悄悄恢复。零实测拉动。
  • 防 AI 犯错(出错时谁看到什么):
    • A:没人看到。AI 写的岗位定义引用了一个已卸载包的权限集,不会有任何反馈。
    • B:启动日志大声点名。
    • C:卸载时当场拒绝,最响亮。
    • D:静默改写作者的元数据,最难发现。
  • 创业阶段不扩散:
    • A 零代码;B 在已有报告上加一类,不新增门禁(报告只记录、不拦截)。
    • C 给卸载新增一个拒绝条件。
    • D 新增一条写环境元数据的路径。

os-decision-facets

  • ① 项目长远合理性:B 与 ADR-0131 D4「引用按名、解析不到要大声说」的既有设计一致;C 同向但改卸载语义;A 留一个静默的洞;D 让包写进别人的定义,和 D6 冲突。
  • ② 实际业务拉动:零实测拉动;撞上它的是卸载了被岗位定义引用的包的运营者。
  • ③ 防 AI 犯错:C 最响亮(当场拒绝),B 次之(启动报告),A 静默,D 静默改写作者元数据。
  • ④ 创业阶段不扩散:A 零代码;B 只在已有报告上加一类,不加门禁;C 加一个拒绝条件;D 加一条写路径。

Prior rulings read: uninstall, ghost, permissionSets, position definition → 4 hits; ADR-0090 D9, ADR-0106 D1 (incidental), ADR-0131 D3; plus ADR-0131 D4/D6 and the ADR-0090 everyone section read by hand; thread: #15204 (6123311533), #22900 (6128268740, 6130417182).

推荐

推荐 B,回退 A。

  • 只看①选 B;②③④ 是否翻转:否。③ 上 C 更响亮,但 C 改了卸载语义,只凭 ③ 不翻字母。
  • 工作量:B 和 C 都是 1 个 PR,规模都没有实测,没有看到 ≥3× 的差距。
  • 置信缺口:

裁后执行


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #336 item 4 · letter B · maintainer 「其他同意」 2026-10-13T11:59Z

    Director seat, summon #41, session_01TJKP6W6UDYEBHtccVdpVoz (GitHub os-steve; written as objectstack-fleet[bot] through the relay, read back). Presented in this seat's chat as batch #336 item 4 with recommendation B (fallback A); the maintainer's word on the remaining four items of the batch was 「其他同意」. The card's premises were re-read by this seat on origin/main before the batch and hold (the boot report's header lists no permissionSets class; position.zod.ts declares permissionSets; stage 8's PR #23222 is an open draft). Thread-read: none.

    B — the reading is confirmed, and the boot report says it out loud. The maintainer's ruling 6123311533 ("user or position grants") covers grant rows, not position definitions: a definition is the author's metadata (ADR-0131 D3), and D does not edit it. A position definition that names a permission set the catalog cannot resolve grants nothing (fail closed) and is reported: the catalog reference report gains a sixth class, "position definition names a permission set absent from the catalog", listed per position, which clears by itself on reinstall. C (refuse the uninstall) is not taken: a code-declared, sealed position referencing the package's set would make the package un-uninstallable without a redeploy of the other package. D (rewrite the author's definitions) is not taken: it writes into the author's metadata and cannot reach sealed definitions (D6). A is not taken: a silent hole.

  2. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Closed completed at 2026-10-13T12:09Z by the director seat (summon #41, session_01TJKP6W6UDYEBHtccVdpVoz, written as objectstack-fleet[bot] through the relay): the ruling is recorded (6145039294, letter B, the maintainer's 「其他同意」), the reading of 6123311533 confirmed, and the execution lives on 8-post #22975 as its closeout item (6145194655: the catalog reference report's sixth class after S8 lands); the epic ledger carries the line (#15204, 6145185086). needs-user-decision left with the ruled transition; domain:engine, priority:p2, target:v18 and area:access stay as attribution.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:enginepriority:p2Medium: important, M3target:v18

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions