在一个同时启用 Evidence、Memory、Graph Proposal 和 ReviewDecision 的平台里,memory:admin 应不应该顺便管理 Registration?
直觉上可能会说“可以,反正它是管理员”。但这句话把一个领域权限偷换成了平台超级权限。Memory 管理员可以管理 Memory,不代表它能够创建产品身份、授予 Graph executor、读取 Evidence authority,或改变 Registration。
Agent Foundation 的 AF-RA01 corrective 就来自这个反例:公共 Product Runtime 已经采用 capability-neutral 的 V2 request 和 binding,但响应与 credential handoff 仍继承早期 Memory-shaped ExternalProjectRegistrationV1。表面看是某个 HTTP 403“不方便”,真正的问题却是 Registration 权限模型没有跟上平台能力模型。
查看 Agent Foundation 项目总览 · 打开本文交互图
最小反例不是“管理员为什么被拒绝”
正确的最小反例是:
- 使用仅拥有
memory:admin的 credential; - 尝试创建或修改 Product Runtime Registration;
- 期望结果必须是 403;
- Registration、Evidence、Graph Proposal 与 Graph 均不得发生写入。
如果这个请求成功,说明平台把 Memory 领域管理员错误提升成了 Foundation 超级管理员。若只在 HTTP route 上加一个特殊判断,底层 authorization lattice 仍可能在 SDK、内部 facade 或新 route 中再次泄漏。
所以修复不能是“再加一条 deny rule”,而要找到 invariant 最早变假的位置。
最早失真层:Registration 仍在说 Memory 的语言
V2 Product Recipe 可以声明不同能力,Runtime assembly 也能找到不同 component owner。但旧 handoff 仍以 Memory namespace、Memory scopes 和 Memory admin 解释注册后的权限。
这会产生三个后果:
memory:admin被过度提升;- reviewer/executor 的 scopes 可能由共享 ReviewDecision 反向推导,而不是目标 authority 授予;
- 产品得到的像是一组可直接使用的凭据,而不是待 provisioning 的角色上限。
修复发生在 Registration 公共合同,而不是各 adopter 的 adapter。
ProductRegistrationReceiptV2 只描述“最多可以有什么”
新的 public receipt 返回 actor_scope_requirements。每个启用角色——agent、reviewer、executor、revalidator——都有独立的 namespace 与 maximum scopes。
Receipt 不包含 secret,不是 credential,也不表示调用者已经拥有这些权限。部署方仍需为实际 principal 配置独立 credential;每次 open 或调用时,再计算三个集合的交集:
- actor role 允许的 scopes;
- sealed binding set 为该角色冻结的 scopes;
- 实际 credential 持有的 scopes。
只有交集中的能力可以被调用。
Compiled plan、accepted claims、唯一 Runtime owner 与 sealed binding 共同派生 Registration;receipt 只把角色上限交给部署方。
谁授予 reviewer 和 executor 权限
共享 ReviewDecision 负责记录不可变审批,但它不是所有目标对象的权限来源。
Memory review scope 应由 Memory authority 授予;Graph Proposal review scope 应由 Graph Proposal authority 授予。ReviewDecision 只消费已经被授权的 target-specific review 请求。
同样,proposal_only 模式没有 reviewer 或 executor,就不应在 receipt 中生成这两个角色。Graph-only Recipe 也不能获得 Memory 或 Relation scope。
这个规则避免了一个常见错误:因为多个领域共用审核基础设施,就把基础设施误当作业务授权中心。
管理权限必须按域存在
AF-RA01 引入 registration:read 与 registration:write,并保留 Evidence、Memory、Graph Proposal 等各自的 admin/review/effect scopes。
这并不是把权限名称拆得越细越好,而是保证每项高风险动作都有明确 owner:
- Memory 管理员不能创建 Registration;
- Registration 管理员不能自动写 Graph;
- reviewer 不能执行 outbox;
- executor 不能自行批准 proposal;
- revalidator 只能处理已经授权的 dependency convergence。
平台不存在一个由领域 scope 隐式获得的超级管理员。
为什么没有新建 V3 或数据库迁移
纠偏过程中,一个诱人的方案是新增 RegistrationV3、新表和迁移,借版本号把旧问题隔离出去。但持久化记录已经能够表达 sealed binding;失真发生在公共响应、handoff 和 authorization interpretation。
在没有持久化形状变化时增加 migration,只会扩大升级成本而不修复核心 invariant。因此 AF-RA01 保留 V1 兼容 carrier,为新的 V2 注册使用新的 public receipt,并让服务端每次重新读取 Registration。
这也是 first-principles repair 的一个例子:修最早拥有 invariant 的层,不用新版本号掩盖旧抽象。
验收关注的是越权与零写
这一批验证不是只看 happy path。关键证据包括:
memory:admin调用 Registration route 得到 HTTP 403;- 失败前后 Registration 和语义 authority 无写入;
- Graph proposal 在 submit/reviewer 阶段保持 Graph 零写;
- 独立 executor 才能消费 outbox;
- public installed workflow 能返回并校验 receipt;
- 完整 backend/SDK 与现有 adopter 回归通过。
这证明 capability-neutral Registration 合同已经在本地已版本化基线中成立。它不证明生产 IAM、secret rotation、跨租户隔离或组织级 RBAC 已经完成。
这次故障带来的判断
如果一个平台已经支持多种能力组合,它的 Registration 就不能继续以某个最早 adopter 的领域模型为中心。否则每增加一个 adopter,都会出现新的“特殊管理员”“兼容 handoff”和隐式 scope。
我的结论是:Registration receipt 应当是能力编译结果的受限公开投影,而不是凭据包;管理员应按 authority domain 存在,而不是由最早出现的 admin scope 继承整个系统。 这次纠偏没有增加业务功能,却让以后新增能力时不必继续打权限补丁。
