Skip to content

AREA_REQUIRED_PERMISSIONS_RETIRED 的处方在 #4722 之后过时:仍写着「the server does not walk areas」 #4749

Closed

Description

@os-zhuang

从 #4722 的实现中记录的越界发现(未认领)。

现象

#4722 让服务端权威闸门 filterAppForUser 走 areas[].navigation,area 内导航项的
requiredPermissions / requiresService 现在由服务端剥离。但 packages/spec 里两处
仍然陈述改前的事实:

  1. packages/spec/src/ui/app.zod.ts:662(AREA_REQUIRED_PERMISSIONS_RETIRED 处方正文):

    requiredPermissions / requiresService on a navigation ITEM are stripped server-side
    from the app's top-level navigation tree and re-checked in the shell. Items nested
    under areas[] are gated in the shell only — the server does not walk areas — so
    anything that must never reach the browser belongs in the top-level tree, or in its own app.

    这段话是作者写错 area 级键时唯一能读到的处方(strict schema 的 unknown-key 报错正文),
    现在它把一个已经关闭的缺口描述成仍然存在。方向上是「过度保守」而非不安全(它劝作者把
    敏感项挪到顶层树,这仍然可行),但它是错的,而且是说给作者听的那一份。

  2. packages/spec/src/ui/app.test.ts:1374 有一条断言把这个措辞钉死:

    // The honest caveat: the server does not walk areas, so an item gate
    // INSIDE an area is shell-side only.
    expect(msg).toMatch(/shell only|does not walk/is);
    

    所以改处方正文必须同时改这条 pin —— 两者是一个耦合改动。

为什么没在 #4722 里顺手改

#4722 的派发约束是 packages/spec/** 零改动(单文件例外只开给
packages/spec/liveness/app.json 的账本 note,已按预案改写)。处方正文是 .describe 级别的
用户可见字符串,改它要连带 pin 与 spec 生成物(gen:schema / gen:docs)一起动,正是
发布窗口里该避开的涟漪。故按 Prime Directive #10 独立记录。

建议的修法(一次改完)

  • 改 AREA_REQUIRED_PERMISSIONS_RETIRED 正文:项级 requiredPermissions / requiresService
    在两棵树(顶层 navigation 与 areas[].navigation)都由服务端剥离(filterAppForUser 只走 app 顶层 navigation —— areas[] 里的 nav 项权限过滤仅客户端生效(#4651 移除假闸门后剩下的真缺口) #4722);仍然只在
    客户端求值的是 visible(CEL)与 requiresObject —— 必须永不到达浏览器的东西写
    requiredPermissions,不要写 visible。
  • 同步改 app.test.ts 那条断言(改成钉新的正确措辞,别只是删掉)。
  • 跑 pnpm --filter @objectstack/spec check:generated,按它报的 stale 项重新生成。
  • 顺带核对 AREA_VISIBLE_RETIRED 正文:它指向项级 visible,那一层依然只在客户端求值,
    所以那段大概率不用改 —— 确认一下即可。

区分清楚:退役的 area 级键(areas[].visible / areas[].requiredPermissions)不复活,
本单只是把处方里关于 area 内部项级闸门的那句事实改对。

参考:#4722(实现)、#4651(退役裁决)、packages/spec/liveness/app.json 的
areas.navigation note(已在 #4722 里改写,可作为正确措辞的蓝本)。

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    and removed
    domain:specDomain lane — seat card indexed by label:pm:seat
    on Aug 3, 2026
  2. added
    target:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA
    on Aug 4, 2026
  3. self-assigned this
    on Aug 4, 2026
  4. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    认领:PM 循环第 1 轮(v17 ci-tooling 工作集 / C 包 #5163)
    会话:session_01FTszibd6C8sUCCZnM4VcrL
    分支:claude/issue-4749-area-prescription-stale
    Worktree:objectstack-issue-4749
    域:domain:spec-tooling
    文件面:packages/spec/src/ui/app.zod.ts(仅 AREA_REQUIRED_PERMISSIONS_RETIRED 处方正文)、packages/spec/src/ui/app.test.ts(pin 断言换钉)、spec 生成物(check:generated 整体重生成,含 content/docs/references/ui/*)、.changeset/*

    ⚠️ 触 zod 文件 —— 已按 #5163 规则在卡上留言协调。⚠️ 同日 churn:app.zod.ts 今日已被 #5250 / #5246 改动,派发词含「先核对 origin/main 现状,issue 引用的行号可能已移位」。


    Generated by Claude Code

  5. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    PM 复核(第 1 轮):PR #5336 —— 主体通过,一处扩范围返工中

    会话 session_01FTszibd6C8sUCCZnM4VcrL。逐条对 GitHub 与 origin/main 实况核验(不取报告自述):

    通过项

    返工项(一处,已回派同一 dev、同 worktree 续做)

    AREA_VISIBLE_RETIRED 的收尾句把服务端强制落点枚举成「app 本身 + 顶层 navigation 的项」两处,#4722 之后漏了 areas[].navigation 的项。dev 按派发的文件面(仅 AREA_REQUIRED_PERMISSIONS_RETIRED 一个字符串)没改,并如实报告 —— 这是 PM 派发时把文件面收窄过头,不是 dev 越界,本单正文本就要求「确认 AREA_VISIBLE_RETIRED」,确认结果是需要改,那它就在本单范围内。已扩范围折进 PR #5336:同文件、同常量块、约六个词,另开 PR 按同文件串行规则要等本 PR 合入,为六个词付一整轮;更要紧的是留着它 = 一条处方改对、紧邻的另一条留错。

    越界发现 #5337:已核验属实并入队(收窄为 migrations/registry.ts 的 protocol-17 rationale + 其投影 docs/protocol-upgrade-guide.md:173 + 未发布 changeset;PM 已裁定 changeset 一并改口径)。

    返工提交与 CI 全绿后转 ready 入队。


    Generated by Claude Code

  6. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    PM 复核(返工轮):ACCEPT → PR #5336

    会话 session_01FTszibd6C8sUCCZnM4VcrL。返工项已按裁定落地(提交 23ace2690),对 GitHub diff 逐条核验:

    • AREA_VISIBLE_RETIRED 的枚举补全为「app 本身 + 两棵导航树的项」,app.test.ts 对应 case 换钉不是删钉(钉 either navigation tree / areas[].navigation / #4722),并对旧枚举加负向断言。
    • 反向验证按要求单独做了一次,方向事前预判为红:仅还原 AREA_VISIBLE_RETIRED 一块(保留第 1 轮修复)→ AssertionError: expected … to match /either navigation tree/s,Tests 1 failed | 97 skipped,还原后复绿。第 1 轮 /BOTH trees/ 那次仍然有效。
    • changeset 按要求扩写在既有 .changeset/area-prescription-both-trees.md,未新增第二份。
    • 全量 spec 套件 312 files / 8022 tests 通过;typecheck、eslint、nul-bytes 干净;check:api-surface 首轮红仍是全新 worktree 的 stale-dist 幻影,build 后 9/9 全绿。
    • visible 的口径未被带歪(filterAppForUser 只走 app 顶层 navigation —— areas[] 里的 nav 项权限过滤仅客户端生效(#4651 移除假闸门后剩下的真缺口) #4722 只碰了 requiredPermissions / requiresService),area 级键保持退役、措辞未读成复活。

    两处 dev 的主动判断,我认为都对且值得记下:

    1. 它在处方里把 visible 与 requiredPermissions 的分工写死了 ——「visible 隐藏的是已经发出去的条目,requiredPermissions 才让条目根本不被发出」。理由是走到这条报错前的作者正攥着一个 CEL 表达式,「就近改写成项级 visible」是此刻最顺手也最危险的落点。这是提前堵掉一条安全语义的误读路径,不是文案润色。
    2. 它主动改写 protocol-17 的 migration rationale 仍写着「the server does not walk areas」,并投影进生成的升级指南 #5337,把发现 (a) 标注为已落地并加了「勿重复实现」——否则下一个被派到 protocol-17 的 migration rationale 仍写着「the server does not walk areas」,并投影进生成的升级指南 #5337 的 dev 会返工刚合入的代码。PM 补了一处:该改写把「未发布 changeset 的口径请维护者定」恢复成了原始措辞,而我在分诊评论里已裁定一并改;已在 protocol-17 的 migration rationale 仍写着「the server does not walk areas」,并投影进生成的升级指南 #5337 正文更正,免得留下一个假的未决项。

    worktree 处置也正确:按磁盘纪律拆除后,以同路径同分支恢复(git worktree add <同路径> <同分支>),未新建任何东西,认领/分支/PR 三者不变。

    CI 在 23ace2690 上重跑中,全绿后转 ready 入队。


    Generated by Claude Code

  7. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    已落地 —— PR #5336 经合并队列合入 main(553a47fda)

    会话 session_01FTszibd6C8sUCCZnM4VcrL。落地后复核判据本身:

    $ git grep -c "in BOTH trees" origin/main -- packages/spec/src/ui/app.zod.ts
    1                                              # AREA_REQUIRED_PERMISSIONS 新措辞在
    $ git grep -c "does not walk" origin/main -- packages/spec/src/ui/app.zod.ts
    0                                              # 旧说法在本文件零残留
    $ git grep -c "either navigation tree" origin/main -- packages/spec/src/ui/app.test.ts
    1                                              # 换钉后的 pin 在
    

    ⚠️ 一处读数陷阱,记下来给后来者(本会话一天内第二次踩):对 app.zod.ts 查 either navigation tree 会零命中,看起来像 AREA_VISIBLE_RETIRED 的改动没落地。反查邻近词后真相是它被字符串拼接拆在两行:

    689:  + '`requiredPermissions` instead: on the app itself, or on the ITEMS of either navigation '
    690:  + 'tree — the app\'s top-level `navigation` AND every `areas[].navigation`, both stripped '
    

    运行期组装后才完整,所以 app.test.ts:1472 的 /either navigation tree/ 断言是通过的。处方正文一律是拼接字符串,行式 grep 天然看不见跨行短语 —— 与 #5337 里 migrations/registry.ts 672–674 行完全同形。零命中必须用确定存在的邻近词反查,否则会得出「改动没落地」的错误结论。

    两条处方(AREA_REQUIRED_PERMISSIONS_RETIRED 与扩范围折入的 AREA_VISIBLE_RETIRED)都完好落地,退役裁决未被读成复活。

    剩余半边 #5337 仍然成立,已复核 origin/main:packages/spec/src/migrations/registry.ts:674 那句「walk `areas`」还在,并且仍在投影进 docs/protocol-upgrade-guide.md。该单已入队,派发时以本 PR 落地的措辞为蓝本。


    Generated by Claude Code

  8. added a commit that references this issue on Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looptarget:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions