Skip to content

permission-set backfill (ADR-0094 D4) 现在 100% 失败:行里的 active 存储列喂进了 #4001 之后严格化的 permission spec #4669

Description

@os-zhuang

发现于:cloud 侧把 framework pin 从 ad5fe25 前移到 462b713(cloud#1010)后跑全量测试时,日志里出现的 WARN。
核实基线:objectstack @ 462b713,cloud @ e14553d
未认领 —— 只是记录,谁接手谁 assign。

症状

ee-group-showcase / security-enterprise 的测试里,每次 permission-set backfill 都打出这条:

WARN [security] permission-set backfill into metadata failed (ADR-0094 D4)
  {"name":"d8_qc_user","error":"[invalid_metadata] permission/d8_qc_user failed spec
   validation: <root>: Unrecognized key(s) on this permission set: `active`.
   Until #4001 these were dropped silently — the set still parsed, so the author
   believed a capability boundary was declared that the runtime never saw."}

测试是绿的。 失败在 permission-set-projection.ts:770-773 被 catch 成一条 logger.warn,backfilledIntoMetadata 不加一,然后继续。所以没有任何 gate 会因此变红。

因果链

  1. sys_permission_set 有一个 active 存储列 —— 是 framework 自己声明的,sys-permission-set.object.ts:33 把它放进 highlightFields,还有两个动作靠 bodyExtra: { active: true } / { active: false } 来启停一个 set。它是完全正当的运行时状态。
  2. ADR-0094 D4 的 backfill 走 permissionSetBodyFromRow(row),把数据库行转成 metadata body —— active 跟着进去了。
  3. #4001 把 permission-set 的 spec 形状封成严格键。active 从来不是 spec 上的键,此前被静默 strip,所以第 2 步一直"能用"。
  4. 严格化之后,saveMetaItem 对每一个没有 declared body 的 permission set 抛 invalid_metadata,backfill 全线失效。

不是 showcase 数据特有的问题:任何走这条路径的 set 都会命中,因为 active 来自表结构,不是作者写的。

为什么值得单独立一条

这正是 #4001 报错文案自己描述的那类事故,只是发生在 framework 内部而非授权侧:一个存储列被当成 spec 键送去校验,严格化之后整条投影路径静默停摆。catch + warn 的处理让它没有任何自动信号 —— 我是在读一次无关的 bump 日志时偶然看见的。

顺带一提,cloud 那边新加的 audit-spec-changes.mjs(cloud#995)也抓不到它:审计只扫 spec-changes.json 登记过的 surface,而 permission-set 的严格化没有对应条目。

可能的修法(未验证,留给接手的人判断)

  • permissionSetBodyFromRow() 里显式挑出 spec 认的键,而不是把整行带过去 —— 存储列(active、时间戳、managed_by 等)本来就不该进 metadata body;
  • 或者反过来,如果 active 确实应当是 permission set 的声明语义(而不只是行状态),那就该在 spec 上正式加回来,而不是靠 strip 苟活。

先判断哪一边才是它真正的归属,再动手 —— 这个键同时被表列和两个动作使用,不能只看 backfill 这一处。

复现

# cloud @ e14553d(pin 已在 462b713)
pnpm turbo run test --filter @objectstack/ee-group-showcase
# 测试通过;在输出里搜 "backfill into metadata failed"

Activity

  1. self-assigned this
    on Aug 3, 2026
  2. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    🔒 认领 —— 第 2 轮(发布窗口 bug 批)

    • 会话:session_015Br2xsJsczFsTR9bvbh2Ny(PM 循环)
    • 分支:claude/issue-4669-permission-backfill-strict-spec
    • Worktree:objectstack-issue-4669

    ⚠️ 共用身份,session ID 不符即本单已被占,请另选。

    两个方向的 PM 裁定(维护者可否决):选投影侧挑键(正文方案 1)。理由:

    • active 的现有消费面(表列 + highlightFields + 两个启停动作的 bodyExtra)全是行状态语义 —— 它是运行时开关,不是作者声明的能力边界;进 spec(方案 2)等于把状态提升为契约,方向不对,且是窗口内禁的 spec 变更、属对侧车道。
    • permissionSetBodyFromRow() 改为显式挑 spec 认的键(白名单),存储列(active、时间戳、managed_by 等)全部不进 metadata body。白名单从 spec 的 schema 键表派生而不是手抄一份,否则 spec 加键时这里又静默漏。

    第二半交付(与修复同 PR):这个 catch + warn 正是 #4632 刚定的规则里的第二类降级 —— backfill 全线停摆但测试全绿、无任何自动信号。按新规则(AGENTS.md「Degradation log levels」节)把它变响亮:失败计数进结果、级别提到 error、文案含后果与修复动作。若 check:durability-log-level 的词表覆盖不到这类投影写入,在 PR 里说明(那是闸门已声明的局限,不是你的债)。

    约束:⛔ packages/spec/** 零改动(白名单派生用运行时可及的 schema 导出,不改 spec);⛔ content/docs/releases/ 不碰;资源纪律照旧。cloud 侧复现属参考,本单不碰 cloud 仓。

    验收:backfill 对带 active 列的行成功(backfilledIntoMetadata 计数增长);两个启停动作行为不变;真失败时响亮(测试钉住);正文 WARN 在同场景下消失。


    Generated by Claude Code

  3. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    ✅ PM 验收通过 —— PR #4755,CI 全绿(18 success + 2 skipped,0 失败),已转 ready 并入合并队列

    复核对着 GitHub:

    场外发现处置


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions