门面对等
上级:structure · 邻居:facade-directions · owner-facade-from-registry · ui-mcp-parity
一份注册表;每一个门面要么由它生成、要么对着它校验。 绝不允许同一个能力有第二份手写的描述。
它回答的问题
同一个底层能力,产品要从好几个面暴露出去:admin UI 的 HTTP 路由、owner 的 MCP 工具、API-key 面、访客 agent 的工具集。各写各的就会漂移 —— 而这里的漂移不是外观差异。一个 op 在 admin 面板上有、在 MCP 上没有,意味着 owner 点鼠标能做的事、没法让他的 AI 去做,而那正是这个产品的前提在无声地失效。
这个缺口是量出来的,不是估的:对等机制第一次跑,找出 56 个 op 只在一个面上有、另一个面上没有。事先的估计是十五个左右。这个比例本身就是论证 —— 一处读代码看不出来的不一致,比写这些代码的人猜的多了将近四倍。
机制
一个域把每个 op 声明成数据,只声明一次:
fp.Op{ ID, Description, InputSchema, Kind, Reach, Invoke }
Kind—— Read / Query / Action,各个门面按自己的语汇渲染它(HTTP 的 GET / QUERY / POST)。Reach—— 暴露意图,而且是对着门面的类声明的,从不对着门面的名字:OwnerRead()、OwnerAction()、OutwardRead()、OutwardAction(),或者Only(理由, …)用于真正只该出现在少数面上的 op —— 理由是必填的,于是一个缺口是一次被明确评审过的决定,而不是一次无声的遗漏。
正因为 reach 是对着类声明的,后加的门面会自动绑上。没有人需要再把 op 清单走一遍。
dispatcher 把所有声明汇成那唯一一份注册表(convergence-inbound-and-outbound);paritymanifest 记录每一类门面可以承载什么;一条棘轮兜着还没迁完的文件,只许变少。
为什么"声明一次"胜过"事后审计"
把对等当成一次审计,就是一件得有人记得去跑、而且必须跑全的任务。把对等当成一次投影,它就是一条你违背不了的性质:描述没有第二个地方可写,于是没有第二份描述可以跟它不一致。
剩下的风险不是漂移,是覆盖:一个从没被声明过的 op,对这套机制是隐形的。corpus_graph 就是这么溜掉的 —— 一条既没有 MCP 孪生、也没被登记进那张手写对照表的路由,棘轮从来看不见它。一份注册表只保证它里面那些东西彼此一致。
它不做什么
它不决定一个 op 应不应该出现在某个面上 —— Reach 是人的判断,而 Only(理由, …) 的存在正是为了让那个判断写在 op 旁边,而不是从它的缺席里去反推。对等保证的是:这个答案只说一次,而且处处照办。