Skip to content

E5b(#5040 执行器):接线收拢 —— 派发步 501 分支替换为「策略链 → 执行委派」+ 真实 boot 集成测试 #5129

Description

@os-zhuang

Part of #5040(E 系列 R3 收拢单,小型)。Blocked-by: #5091(E4)+ #5092(E5) —— R3 为避免两单同改 api-endpoint-step.ts 的必然冲突,按模块切面解耦(E4 独占派发步接线、E5 只出纯执行模块),本单在两者落 main 后做最后接线。

范围(小)

  1. api-endpoint-step.ts:命中分支的 501 兜底替换为完整链 —— 匹配 → 策略链(E4,401/429/头已接)→ executeEndpointTarget(E5 模块)→ 响应映射;cacheTtl 计算头应用到真实执行结果上(E4 落地时如何暴露以其模块文档为准);
  2. per-environment metadata/services 解析:按 E5 落地报告的 followup 与 E3 注释(需要 dispatch() 的 resolver;单租户直取本 kernel 服务,多租户路径如超出小单范围,如实报告并留给 E8 前置);
  3. 真实 boot 集成测试(扩展 dispatcher-plugin.endpoint-fallback.integration.test.ts):命中执行(object_operation + flow 各一)、策略拒绝(401/429)短路不执行、未命中/前缀外仍逐字节不变;
  4. 台账 * /apps/** 行注记更新:「EXECUTION IS NOT WIRED」改为如实描述已接线状态(仍注明 publish 门未翻转,结构性不可达);
  5. changeset(@objectstack/runtime minor,仍零现网行为变更)。

约束

执行器车道程序卡(session_01EYGdmvWP1ieZSLqvAW6uyd 按序派发),其它车道请勿认领;词表与 spec 零触碰;E6(#5093)与 E7(#5111)的 Blocked-by 链随本单顺移(E6 实际前置 = 本单)。

关联:#5091、#5092、#5090(seam)、#5040 设计 §3/§5。

Activity

  1. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    E4(#5091 / PR #5135)交接契约 —— 本单要接的那几根线

    E4 已把策略链落在 packages/runtime/src/endpoint-policy.ts,并在 api-endpoint-step.ts 的命中分支里调用。按 R3 文件面切分,E4 不碰 dispatcher-plugin.ts,所以下面这两根线留给本单接:

    1. 兜底器要把策略上下文喂给派发步。 AppEndpointStepInput.policy 目前是可选的,兜底器没传 —— 没传时终态答复仍是 501,因此今天不存在「未经策略就被服务」的请求;但本单把 501 换成执行的同一次改动必须同时传它,否则执行会落在一个策略没跑过的分支上。上下文形状(EndpointPolicyContext):

    • headers / remoteAddress:直接取兜底器的 req(E3 已备好 remoteAddress);
    • resolvePrincipalId:用 security/resolve-session-principal.ts 的 resolveSessionPrincipalId(authService, headers) —— 与 server 级限流器、dispatcher 具体路由挂载同一个查询,别新起一个;
    • limiters:createEndpointRateLimiterRegistry({ resolveCache: () => safeGetService('cache'), logger }) —— 在 start() 里建一次,不要每请求新建(每请求新建会让每端点桶的降级 warn 重复刷屏,且限流器缓存失效);
    • trustProxy:透传 config.rateLimit?.trustProxy === true,与 server 级读同一份声明,不造第二个信任开关。

    2. 兜底器要写出 answer.headers。 AppEndpointStepAnswer 新增了 headers?(429 的 Retry-After 走这里);现在的兜底器只写 status + securityHeaders + json,会把它丢掉。一个丢了 Retry-After 的 429 对客户端等于没说话。

    3. cacheTtl 的头怎么用。 卡面问「E4 落地时如何暴露以其模块文档为准」——答案:不在 501 上应用,挂在裁决对象上。applyEndpointPolicies 返回 { verdict: 'pass', principalId?, responseHeaders },responseHeaders 即 {'Cache-Control': 'private, max-age={秒数}'}(或 no-store,或空对象)。理由:Cache-Control 描述的是尚不存在的成功响应体,让客户端缓存一个 501 比什么都不说更糟。本单把执行结果的成功答复合成时,把 responseHeaders 并进去即可;verdict.principalId 也顺带给了执行委派,不必再查一次会话。

    4. 落点位置是结构性的。 E4 把执行落点安排在策略链之后的分支里,而该分支在没有策略上下文时不可达 —— 本单加执行调用时没有别的地方可放,这正是「接了执行、忘了策略」不可能发生的原因。请保持这个形状,别把执行提到 applyEndpointPolicies 之前或与之并列。

    5. 集成测试。 dispatcher-plugin.endpoint-fallback.integration.test.ts 目前只有 E3 的否定断言(E4 未扩它 —— 兜底器没喂上下文,策略用例在真实 boot 里还构造不出来)。本单接上线后,401 / 429 短路两条真实 boot 用例是本单卡面第 3 条的应有之义。


    Generated by Claude Code

  2. self-assigned this
    on Aug 4, 2026
  3. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    认领 —— PM 会话 session_01EYGdmvWP1ieZSLqvAW6uyd(执行器车道),分支 claude/issue-5129-wire-endpoint-chain,即刻派发。前置齐备:E4(a70cd0a)/ E5(277eb36)均落 main,复核毕。

    文件面声明(#4604):packages/runtime/src/dispatcher-plugin.ts(兜底器喂 EndpointPolicyContext + 写出 answer.headers + limiter registry 构建)、api-endpoint-step.ts(策略通过分支的 501 → executeEndpointTarget)、dispatcher-plugin.endpoint-fallback.integration.test.ts 扩展、route-ledger.ts 注记更新、changeset。移交契约以本单此前 E4 评论 + PR #5136 正文「接线单」段为准。


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions