Skip to content

saveMetaItem 的 direct-active 写入是 api 的第三条门外通路 —— 命名空间门(ADR-0121 D1/D2)与去重门在这条路上无人跑 #5311

Description

@os-zhuang

越范围发现,记录于 #5271 实现期,不在该 PR 内修(#5271 的范围是注册表与 schema 绑定)。与 #5206 的两步都不重合 —— 它是两步都落地之后剩下的那条路。

事实

packages/metadata-protocol/src/protocol.ts没有任何 endpoint-publish-gate 的导入(grep validateApiEndpointDeclarations / identityFreeEndpointGateFailure 在该包内零命中)。而 saveMetaItem 明确支持 direct-active 写入(该文件自己的注释:「saveMetaItem(draft AND direct-active saves)」)。

于是三条通路的门覆盖是这样的:

通路 跑哪道门 命名空间门(D1/D2) 去重门
stack 工件(defineStack({ apis })) 全量门(ObjectStackDefinitionSchema)
publishPackage / publishPackageDrafts(#5189 / PR #5279) 全量门(有 manifest.namespace)
PUT /meta/api/:name,state: active

第三条路唯一的守卫是装载期兜底(#5189 / PR #5203),而它按设计跑的是免身份门 —— 命名空间门与去重门恰恰是它跳过的那两道(门函数自己的 TSDoc 写明了这一点)。

后果

一条运行时直写的 active api 行,可以声明落在别的应用的 carve-out 里的路径(/api/v1/apps/OTHERAPP/...)。免身份兜底不看命名空间,所以它会被正常索引;端点步骤只要求路径在 /apps/ 之下。ADR-0121 D1 的原话是「路由归属靠构造而非靠约定加检查」成立 —— 但这条路上没有任何一处在构造时检查归属。

去重同理:同一 METHOD + 归一化 path 的第二条声明,在装载期由 buildEndpointIndex 按字典序裁决并 error 点名,但没有人在写入当场拒绝。

#5271 不改变这条路的现状,它只是给这条路加了一道形状门(422);servability 的判据本来就不在注册表这一层。

建议(不预判)

两个方向,取舍留给裁决:

判据不应产生第二份 —— 无论走哪条,跑的都得是 endpoint-publish-gate.ts 里那个 firstFailure

关联

#5206(父单)、#5271(step 1,出处)、PR #5279(step 2)、#5189 / PR #5203(装载期兜底,门函数导出)、ADR-0121 D1/D2。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions