Repository navigation
Closed
Labels
bugSomething isn't workingSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loopReady for the PM dispatch looppriority:p2Medium: important, M3Medium: important, M3security
Description
从 #4651 / #4718 分出的独立记录。未指派 —— 只是记录。
现象
服务端权威可见性闸门 filterAppForUser(packages/rest/src/rest-server.ts)只过滤 app 的顶层 navigation 树:
:1814 检 app 级 requiredPermissions —— 不满足 return null
:1823 const nav = Array.isArray(item.navigation) ? item.navigation : null;
if (!nav) return item; ← 没有 navigation 就直接原样返回
:1826 filterNav 递归检 nav 项级 requiredPermissions / requiresService
item.areas 从头到尾没有被读到。所以一个写在 area 内部的 nav 项上的 requiredPermissions / requiresService:
- 服务端不剥离 —— 该条目连同它的
objectName/pageName/componentRef等指向,照常出现在/meta响应里; - 只有客户端
NavigationRenderer.tsx:894的checkPerm会把它藏起来。
也就是说,对 areas 型 app 而言,导航项级权限目前是渲染层的礼貌,不是服务端强制。改一次前端状态、或者直接读 /meta 的 JSON,就能看到本该被 gate 掉的条目。
与 #4651 的关系(这是剩下的那一半)
#4651 处理的是 area 级的两个键 areas[].visible / areas[].requiredPermissions——它们任何一层都没有消费者,是纯粹 fail-open 的假闸门,已按维护者裁决在 17.0.0 走路线 B 移除(#4718)。
本单是另一件事:area 里面的 nav 项闸门是真的被消费的,只是只被客户端消费。#4651 的处方对此没有回避,明写了限定并落进了三处:
- 退役处方本身(
ui/app.zod.ts的AREA_REQUIRED_PERMISSIONS_RETIRED:「Items nested underareas[]are gated in the shell only — the server does not walkareas」); - 活性账本
packages/spec/liveness/app.json的areas.navigation条目 note; - 一条 characterisation pin(
packages/rest/src/rest.test.ts,filterAppForUser — the enforced permission layers (#4651)里的最后一条),它故意把当前行为钉住,并在注释里写明:谁让它开始走 areas,就应该看到这条断言红掉、然后连同账本 note 一起有意识地改写。
所以现状是「已知、已记录、已钉住」,但没有 open issue 跟踪——#4651 已随 #4718 关闭。本单补上这个跟踪位。
需要决定(与 #4651 的路线 A 是同一组语义问题)
要让服务端也过滤 areas,先得回答:
- 一个 area 里的项被过滤光之后,这个 area 本身还返不返回?(返回一个空 area,客户端切过去是一片空白)
- 顶层
navigation与areas[].navigation同时存在时(schema 允许,AppSchema with areas的 backward-compat 用例就是这个形状),两棵树的过滤要不要一致? - 是否顺带补服务端的
visibleCEL 求值?目前visible任何层级都只在客户端求值(AppSidebar.tsx:236经 ExpressionProvider),服务端从不绑定user上下文——所以「服务端强制」在requiredPermissions/requiresService上成立,在visible上不成立,这个不对称本身也值得写进文档而不是让人自己发现。
影响面
filterAppForUser的调用方:/meta列表与单项 GET(后者在rest-server.ts:3298附近另有一次 app 级复检)。- 平台自带的 areas 型 app 有多少、其中多少条 nav 项带
requiredPermissions,需要清点一遍再判严重程度。 - 不影响 app 级
requiredPermissions(服务端强制,feat(spec)!: 移除 app.areas[] 的两个 fail-open 访问闸门 —— visible / requiredPermissions (#4651) #4718 已加 pin)与顶层navigation树的项级闸门(服务端 + 客户端双端,同上)。
关联
#4651(area 级假闸门,已移除)、#4718(实现 PR,含上述三处记录与 pin)、ADR-0045(hidden app 可见性)、ADR-0057 D10(requiresService 能力闸门)、ADR-0049(enforce-or-remove)、ADR-0078(false compliance)
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loopReady for the PM dispatch looppriority:p2Medium: important, M3Medium: important, M3security
Activity
os-zhuang commentedon Aug 3, 2026
🔒 认领 —— 第 2 轮(发布窗口 bug 批)
session_015Br2xsJsczFsTR9bvbh2Ny(PM 循环)claude/issue-4722-filter-app-areas-navobjectstack-issue-4722正文三问的 PM 裁定(维护者可否决):
navigation树对「项被滤光的分组」的既有处理同形(先读代码确认既有行为,照抄;若顶层无此形状,则滤光的 area 剥离 —— 返回空壳没有任何消费价值)。在 PR 里写明你实测到的既有行为。filterNav逻辑复用到areas[].navigation,不复制第二份实现。visibleCEL 服务端求值 → 明确不做,保持客户端求值,但把这个不对称写进文档注释与 characterisation pin(正文的建议采纳)。服务端 CEL 绑定 user 上下文是另一个体量的事,需要时另立。已预期的既有钉:
rest.test.ts里filterAppForUser — the enforced permission layers (#4651)的最后一条 characterisation pin 会红,红了是对的 —— 按它注释里的预案改写断言,并同步改写packages/spec/liveness/app.json里areas.navigation的 note(它现在写着 server does not walk areas,修完必须跟着改,否则账本说谎)。packages/spec/liveness/app.json仅此一个文件、仅该 note 行 —— 这是账本记录,不是契约 schema。除此之外packages/spec/**仍零改动。此例外已在分片登记与轮报中记录。其余约束:退役键
AREA_REQUIRED_PERMISSIONS_RETIRED(area 级)不复活 —— 本单只管 area 里面的 nav 项级键。⛔content/docs/releases/不碰;资源纪律照旧。验收:areas 型 app 下,无权限用户的
/meta响应里被 gate 的 nav 项(含其 objectName/pageName 指向)不再出现;requiresService同理;顶层树行为回归不变;pin 与账本 note 已按预案改写。Generated by Claude Code
os-zhuang commentedon Aug 3, 2026
✅ PM 验收通过 —— PR #4751,CI 全绿(20 success + 2 skipped,0 失败),已转 ready 并入合并队列
复核对着 GitHub:
resolveRegisteredServices探测面同步下钻 areas —— 这是 dev 自己发现的正确性前提(未探测的服务名 = 「服务不存在」会误剥离合法条目),不是顺手改,论证成立且有专门 pinliveness/app.json账本 note 同步(spec 单文件例外,实测其余packages/spec/**零改动;Spec property liveness 门禁绿)visibleCEL 不对称按裁定只记录不实现,三处落档场外发现 #4749(spec 退役处方正文陈旧)归 spec 车道,留待对侧;本会话按暂停指示不派。
Generated by Claude Code
fix(rest): enforce nav-item gates inside `areas[]`, not just the top-…
fix(spec): fold the sibling prescription in — AREA_VISIBLE_RETIRED's …
fix(spec): 两条 area 退役处方在 objectstack-ai#4722 之后改口径 —— 项级闸门在两棵树都由服务端剥离 (…
fix(spec): protocol-17 rationale states the CURRENT area-gating fact — …
refactor(spec)!: strip internal tracker ids from the three customer-f…
docs(spec): nine migration-step rationale passages and two registry c…
fix(spec): the protocol 16 → 17 upgrade rationale states each cited d…
test(spec): security, ai, identity, integration, migrations, marketpl…