Skip to content

E7(#5040 执行器):翻转 —— publish 硬拒收窄为「不支持子集 + 命名空间门」,声明式端点随 v17 放行执行 #5111

Closed

Description

@os-zhuang

Part of #5040(E 系列第 7 单,发布目标 v17,维护者裁决 2026-08-04:执行器直接进 v17,v17 切版等 E 系列全绿)。Blocked-by: #5091(E4)+ #5092(E5)+ #5093(E6) —— 执行面齐备前 publish 不得放行。

范围(以 #5040 设计与 ADR-0121 为准,spec 车道)

  1. 收窄 声明式 apis:(ApiEndpoint)入站面全链路零执行:元数据装载成功、路由从未挂载、matchEndpoint 全仓无实现 #4936 的整面硬拒(packages/spec/src/stack.zod.ts):非空 apis: 不再一律拒绝,改为逐端点过门 ——
    • 支持子集门:仅放行 17.x 实际执行的形状(E2–E5 已落的匹配与委派能力);词表里声明了但执行器不支持的组合仍响亮拒绝(错误文案指向具体键与替代方案,不留 declared≠enforced 缝隙);
    • 命名空间门:path 必须落在本包命名空间的挂载前缀下(/api/v1/apps/<ns>/…),跨命名空间/越界路径拒绝(ADR-0076 一路一主在 publish 时即成立);同栈内重复 METHOD+path 拒绝(运行时的字典序裁决只兜 publish 之外的直写)。
  2. 生成物整体重生成(os-regen 四步;错误文案、spec-changes、upgrade-guide 均会动)。
  3. 升级文档 = 安全承载件(维护者裁决:不加激活开关,靠文档让 AI 维护者知情):upgrade-guide 条目必须显式包含 —— v16 休眠 apis: 配置在 v17 会被校验并激活;指令 AI 维护者审视历史配置,尤其显式 authRequired: false 条目(词表缺省即 true,E1 已物化,漏写不构成风险);不改 content/docs/releases/(Prime Directive,发版说明由 changeset 集中编译)。
  4. Changeset:@objectstack/spec 相应级别,明确「v17 破坏面 = 校验从整面拒绝改为条件放行」。

约束

词表冻结(ApiEndpointSchema 零改动 —— 门是校验逻辑,不是新键);执行器车道程序卡(session_01EYGdmvWP1ieZSLqvAW6uyd 按序派发),其它车道请勿认领。

关联:ADR-0121、#4936(被收窄的裁决)、#5089/#5090/#5091/#5092/#5093、#5040 设计 §6。

Activity

  1. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    来自 E4(#5091 / PR #5135)的 publish 门补充项 —— 四条,第 1 条是安全相关

    E4 把三个策略键接进运行时时,在词表侧撞到几处「运行时能答、但应该在 publish 就被打回」的形状。按「归挂不散落」记在本单(它们都落在本单收窄硬拒的完成范围内),不另开卡:

    1. ⚠️ D6「authRequired: false 必须伴随 rateLimit」的门必须校验 enabled === true,而不只是「键存在」。
    RateLimitConfigSchema.enabled 的 schema 默认值是 false(packages/spec/src/shared/http.zod.ts:131)。因此这样一条声明:

    authRequired: false
    rateLimit: { windowMs: 60000, maxRequests: 100 }

    parse 之后是 enabled: false —— 一个完全不计量的匿名端点,却满足了「伴随 rateLimit」的字面要求。运行时这边按声明即执行的读法处理(deriveBucketConfig 返回 null,不计量,E4 有测试钉住),所以这道缝只能在 publish 门堵:门的判据应为 rateLimit?.enabled === true,拒绝文案带处方(「匿名端点必须显式 rateLimit: { enabled: true, … };只写窗口和配额不会启用限流」)。ADR-0121 D6 立这条门正是为了匿名面不被白嫖,按字面实现会让门形同虚设。

    2. 端点级 rateLimit 的 maxRequests / windowMs ≤ 0 应在 publish 拒绝。
    server 级预算由 deriveBucketConfig 在 boot 期抛错挡住;端点级没有 boot 期这一说,E4 的裁断是 fail closed —— 每次命中该端点都抛错(经兜底器成 5xx),因为「作者要求计量、实际不计量」是更坏的结果。但作者应该在 publish 就被告知,而不是上线后每请求一个 5xx。文案可直接复用 server 级那两段(零预算会连自己的健康检查都拒;要关限流请写 enabled: false)。

    3. 负数 cacheTtl 应在 publish 拒绝。
    cacheTtl: z.number().optional(),没有 .int() 也没有下界,所以 -5 合法。运行时不能不答,E4 的裁断是负值与 0 同义 → Cache-Control: no-store;但负的「响应缓存秒数」在授权面上是没有意义的一句话,应在 publish 打回。(小数同理未拒,E4 按 Math.floor 截断成整秒 —— 这个可以不拒,截断是无歧义的。)

    4. 非 GET 端点上的 cacheTtl(设计 §3.3 已列在本单清单里)——确认保留。运行时侧 E4 已做成「不发头 + warn 点名端点」,不静默丢弃;门落地后这条 warn 就只会在绕过 publish 直写 metadata.register() 的路径上出现。

    以上四条都不需要动词表(ApiEndpointSchema 零改动),纯校验逻辑,与本单卡面「门是校验逻辑,不是新键」的约束一致。


    Generated by Claude Code

  2. added
    target:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA
    on Aug 4, 2026
  3. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    E5c(#5137 / PR #5167)交来的 publish 门清单 —— 映射键部分

    E5c 已把 inputMapping / outputMapping 接进执行链(packages/runtime/src/api-mapping.ts)。runtime 侧对无法服务的声明给结构化 501 NOT_IMPLEMENTED(点名条目,带处方),但那只是「绕过 publish 直写 metadata.register()」的兜底 —— 本门才是作者应该遇到的第一道关口。请把下列三类并入本单的「不支持子集」硬拒(runtime 侧的判定逻辑集中在 mappingDeclarationRejection,可逐条照读):

    1. transform 子键(设计 §3.4 已定,此处仅确认落项):全仓无「transformation function name」注册表;处方 = 用 source/target 的搬运改名能力,函数式变换待 transform 注册表另行立项;
    2. 不可用的 source / target 路径:空串、空段(a..b、前后缀点)、JS 原型键(__proto__ / prototype / constructor);
    3. 互撞的 target:同栈同一端点内两条写同一路径,或一条写进另一条内部(x 与 x.y)—— 后者会静默丢弃前者,是典型的「写了但没生效」。

    两个留给本单裁决的问题(runtime 侧未擅自发明)

    • inputMapping 的来源域:词表 describe 写的是 Map Request Body to Internal Params,E5c 因此只读请求体,未采纳设计 §3.4 提过的 {...query, ...body}(合并会凭空发明一条谁覆盖谁的优先级规则)。若维护者认为映射也应看见 query,那是对现解读的兼容放宽,加来源即可;反向则是破坏性收紧。
    • 声明了但注定不生效的组合:inputMapping 写在不读 body 的操作(object_operation 的 find / get / delete)上时,今天是一条合法但什么也不做的声明 —— 与 cacheTtl 写在非 GET 上同形(本单已收 GET-only 拒绝),建议一并按同样的姿态处理。

    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    target:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA
    on Aug 4, 2026
  5. self-assigned this
    on Aug 4, 2026
  6. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    认领 —— PM 会话 session_01EYGdmvWP1ieZSLqvAW6uyd(执行器车道),分支 claude/issue-5111-publish-flip,即刻派发。前置 E1–E6 + E5b/E5c 全部落 main(至 1203bb2),执行链在真实 boot 上验证通过。

    两项词表裁决随认领落定(依程序自身原则,PM 裁定):

    1. inputMapping 声明在不读 body 的操作上(find/get/delete)→ publish 拒绝,与 cacheTtl 于非 GET 同类同判(「声明合法、执行惰性」正是本程序要消灭的状态);
    2. inputMapping 的 source 域维持 body-only(E5c 已实现的窄读)—— 门无需动作,将来放宽是词表加法。

    文件面声明(#4604):packages/spec/src/stack.zod.ts(硬拒收窄为逐端点门)+ 生成物整体重生成(os-regen 四步,spec-changes / upgrade-guide 预计移动)+ 门测试 + changeset。升级文档为安全承载件(维护者裁决 2026-08-04):升级指引必须含「审视历史 apis: 配置、尤其显式 authRequired: false 条目」的 AI 维护者指令。


    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

Labels

domain:specDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:dispatchedDispatched to a dev agent by /pm-dispatchprotocol:breaking

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions