Skip to content

capability 无 DEFAULT_METADATA_TYPE_REGISTRY / schema 条目 —— PUT /api/v1/meta/capability/:name 接受任意 JSON 且 /meta/types 报 allowRuntimeCreate:true(与 #5271 的 api 同形) #5961

Description

@baozhoutao

在实现 #5870(把 capabilities 接进 ObjectQL 的 provenance 盖章缝)时发现,未在该 PR 中修复,按 Prime Directive #10 记录。未指派 —— 无人在做。

证据(均读自 origin/main a6b3ee7)

capability 既不在 DEFAULT_METADATA_TYPE_REGISTRY(packages/spec/src/kernel/metadata-plugin.zod.ts,permission / position 都在,见 ~775-776),也不在 BUILTIN_METADATA_TYPE_SCHEMAS(packages/spec/src/kernel/metadata-type-schemas.ts,Security Protocol 段只有 permission / position),也不在 HAND_CRAFTED_SCHEMAS(packages/metadata-protocol/src/protocol.ts ~189,只有 object)。三处皆缺,导致两个后果:

  1. 写入门无校验。 isRuntimeCreateAllowed()(protocol.ts ~6810)在 RUNTIME_CREATE_ALLOWED_TYPES 未命中后走这条兜底:

    // Runtime-registered types (no static registry entry) are
    // synthesised by getMetaTypes() with allowRuntimeCreate=true;
    // mirror that here so /api/v1/meta and PUT /api/v1/meta agree.
    if (!this.STATIC_REGISTRY_TYPES.has(singular)
        && !this.STATIC_REGISTRY_TYPES.has(type)) {
        return true;
    }

    STATIC_REGISTRY_TYPES 由 DEFAULT_METADATA_TYPE_REGISTRY 派生,不含 capability,所以该分支恒真。又因 getMetadataTypeSchema('capability') 返回 undefined,saveMetaItem 走它自己那条「未注册类型 → 不校验直接存」的分支 —— 于是 PUT /api/v1/meta/capability/:name 接受任意 JSON 落进 sys_metadata。这正是 [spec] api 补进 DEFAULT_METADATA_TYPE_REGISTRY 与 BUILTIN_METADATA_TYPE_SCHEMAS(#5206 第 1 步,拆单) #5271 给 api 修掉的同一形态(该 issue 的结论写在 metadata-type-schemas.ts 的 api: 条目注释里)。

  2. /meta/types 合成一个假描述符。 getMetaTypes()(protocol.ts ~2988)对无注册条目的类型合成 allowRuntimeCreate: true、supportsOverlay: false、schema: undefined、label: 'capability' 的最小描述符。Studio 的 metadata-admin 引擎据此渲染一个无 schema 的 raw-JSON 文本框创建表单。

与 #5870 的关系(重要,别误判为回归)

写入门的判定只读注册表、不读 item store,所以这条路在 #5870 之前就已敞开 —— #5870 不会打开它。#5870 改变的只是可见性:capabilities 现在真的会注册进 SchemaRegistry,于是 capability 开始出现在 getMetaTypes() 的枚举里(此前包声明的 capability 根本没进过 registry,该类型从不出现)。也就是说这是一个既存缺陷,被 #5870 从不可见变为可见。#5870 的 changeset 已就「运行时可创建性未改变」这一点作了明确陈述。

为什么值得修

sys_capability 是授权面。一条未经 CapabilityDeclarationSchema 校验就落库的 capability 行,其 name 可以不满足 schema 的 ^[a-z][a-z0-9_.]*$,scope 可以是任意字符串 —— 而 systemPermissions / requiredPermissions 是按 name 字符串解析的。declared ≠ enforced 的一个变体:声明面有 Zod,写入面没有。

建议修法(需裁决,故不夹带)

至少三个选项,取舍不属于 #5870 的范围:

  • A. 补 BUILTIN_METADATA_TYPE_SCHEMAS['capability'] = CapabilityDeclarationSchema + 一条 DEFAULT_METADATA_TYPE_REGISTRY 条目并显式设 allowRuntimeCreate: false(与 ADR-0066 D1「packages DEFINE capabilities」一致:capability 由包声明,不由管理员在运行时凭空创建)。这条同时关掉无校验写入门与假的可创建描述符。
  • B. 只补 schema,让 422 校验生效,但保留运行时可创建 —— 需要先确认真有「管理员运行时新建 capability」的业务拉力。
  • C. 判定 capability 根本不该出现在 /meta/types,改合成逻辑而非补条目。

另需注意:MetadataTypeSchema(metadata-plugin.zod.ts ~72 的 z.enum)也不含 'capability',A/B 两案都要一并处理,并按 AGENTS.md 重新生成 spec 的产物(check:authorable-surface / check:docs / check:api-surface)。

同类邻居(同样缺注册条目、同样走合成分支)还有 role / profile / policy —— 它们连 PLURAL_TO_SINGULAR 映射都没有,以复数键注册。是否一并处理请一并裁决。

Activity

  1. claude commented on Aug 6, 2026

    @claude
    Contributor

    分诊:决策箱(needs-user-decision)+ 域 domain:spec。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. claude commented on Aug 7, 2026

    @claude
    Contributor

    Disposition (2026-08-07): returned to the queue — ADR-0066 D1 already answers the part that looked like a decision.

    Direction A: give capability its schema and registry entry, with allowRuntimeCreate: false. The half that is uncontroversial (a CapabilityDeclarationSchema plus the enum entry) closes an unvalidated write path into an authorization surface — PUT /api/v1/meta/capability/:name currently accepts arbitrary JSON, and systemPermissions / requiredPermissions resolve capabilities by name string. The half that looked like a ruling — whether capabilities may be created at runtime — is settled by ADR-0066 D1 ("packages DEFINE capabilities"); citing an existing ADR is not a new decision. The boolean is one line and trivially reversible if that ever changes.

    Follow api's treatment (#5271) as the template: registry entry, schema, MetadataTypeSchema enum, then regenerate the three check baselines.

    ⛔ Do not ride role / profile / policy along — they lack even PLURAL_TO_SINGULAR entries and are a different shape; bundling would turn an M into an L. File them separately if they need it.

    Operator: PM session session_01GcjbQLUQKysMU9uXB34iyv; maintainer ruling 2026-08-07 (decision-inbox round 2). Veto window open — comment or reopen to overturn.


    Generated by Claude Code

  3. hotlong commented on Aug 7, 2026

    @hotlong
    Contributor

    Release-board audit (maintainer-directed re-audit of non-board domain:spec items, 2026-08-07): adding target:v17 — criterion ①: PUT /api/v1/meta/capability/:name stores arbitrary unvalidated JSON into sys_metadata on an authorization surface (names resolved by string). Ruling A already recorded (registry + schema entry, allowRuntimeCreate: false). Triage seat may veto.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions