Skip to content

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

Closed

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. added
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Aug 3, 2026
  2. self-assigned this
    on Aug 3, 2026
  3. 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

  4. 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

Labels

bugSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looppriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions