Skip to content

feat(runtime): 端点链接线 —— 策略 → 执行,兜底器全上下文(#5040 E5b) - #5156

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5129-wire-endpoint-chain
Aug 4, 2026
Merged

feat(runtime): 端点链接线 —— 策略 → 执行,兜底器全上下文(#5040 E5b)#5156
os-zhuang merged 1 commit into
mainfrom
claude/issue-5129-wire-endpoint-chain

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #5129
Part of #5040(E 系列 R3 收拢单)。前置齐备:E4 a70cd0a、E5 277eb36 均在 main。

做了什么

把 E4(策略键)与 E5(执行模块)接成一条链,并把兜底器一直缺的三根线补齐。现网行为零变更:非空 apis: 在 publish / validate 仍被硬拒(E7 前不撤),整条链结构性不可达。

1. api-endpoint-step.ts:策略通过分支 → 真正执行

命中分支的 501 换成 buildEndpointExecutionContext + executeEndpointTarget,答复按执行结果映射。落点仍在策略链之后的那个分支里 —— 该分支没有策略上下文时不可达,denial 会短路它,所以「接了执行、忘了策略」不是一次改动能靠遗漏做到的事(E4 交接契约第 4 条,形状保持不变)。

verdict.responseHeaders(cacheTtl 算出的 Cache-Control)并进成功答复。错误答复一律不带:这个头描述的是「客户端可以重用的响应体」,让客户端把一个 401 / 429 / 400 缓存 30 秒比什么都不说更糟。

两个「调用方没喂够东西」的 501 分支(无策略上下文 / 无执行接线)保留,措辞改成如实描述。它们在组合后的运行时里不可达 —— 这一点由集成测试钉住,而不是靠删掉诚实的报告来保证。

2. dispatcher-plugin.ts:兜底器全上下文

  • 限流注册表在 start() 里建一次,与 server 级限流器共用同一个 resolveCache。每请求新建会让降级 warn 每请求刷一次,并且丢掉限流器的桶缓存 —— 一个每次调用都重置的预算不是预算。
  • 每请求喂完整 EndpointPolicyContext:req.headers / req.remoteAddress / resolveSessionPrincipalId(与 server 级限流器同一个会话查询,不另起一个)/ 注册表 / config.rateLimit?.trustProxy(与 server 级读同一份声明)/ ctx.logger
  • 写出 answer.headers:此前兜底器只写 status + securityHeaders + json,把 429 的 Retry-After 丢了。一个丢了 Retry-After 的 429 等于没说话。
  • 执行依赖:callDataEndpointExecutorDeps 的文档绑定 —— (action, params, driver, scope, ec) => callData(execDeps, action, params, driver, scope, ec),execDeps 是 dispatcher 已经在给每个域模块用的那一个对象(新增只读访问器 HttpDispatcher.actionExecutionDeps,窄口暴露 ActionExecutionDeps 视图)。automation每请求解析、不缓存(bug(service-automation): flow 节点类型校验跑在插件贡献的执行器注册之前 —— 每个 ADR-0019 approval flow 都被误报「will fail at execution time」 #4771 类),且用不带 envId 的查法 —— 与 POST /automation/:name/trigger 读同一个占位者,否则「同操作同答案」(§4)就不成立了。metadata 则带 envId 解析,与 callData 为同一请求做 ADR-0049 暴露闸时的查法一致。

3. 身份 / 环境:复用 dispatch() 的解析(多租户姿态)

dispatch() 里那段「环境解析 + kernel 切换 + 身份解析」原地抽出为公开方法 HttpDispatcher.resolveRequestScope(context, cleanPath),dispatch() 改为调用它,行为逐字节不变;兜底器调用同一个方法。抄一份到消费者侧会是第二份更弱的身份解析,而 #4936 正是「数据调用没有 ExecutionContext」的现场记录:它会以 system 身份读、绕过 RLS。

顺序是结构性的:解析必须在匹配之前。多租户 host 上 matchEndpoint 必须问本请求自己的 kernel,否则一个租户的声明会决定另一个租户的 URL。

弃权姿态(本 PR 采用):isMultiTenantHost() 为真且解析不出 environmentId 时,该步写不出任何东西(保留传输层原本的 404)并 warn,而不是拿默认 kernel 的数据作答。多租户验收随 E8。

代价控制:所有解析都在 isAppEndpointPath() 之后才发生 —— 碰不到端点保留段的未匹配请求,今天花多少,现在还是花多少(零)。

4. 台账

* /apps/** 行的「EXECUTION IS NOT WIRED」改为如实描述:已接线(策略链 → 委派),script / proxyinputMapping / outputMapping 不执行且答 501,publish 门仍未翻转所以结构性不可达(#5078 纪律)。

验证

pnpm --filter @objectstack/runtime test        → 88 files / 1270 tests passed
pnpm --filter @objectstack/runtime typecheck   → tsc --noEmit,干净
pnpm --filter @objectstack/plugin-hono-server test → 14 files / 164 passed
pnpm --filter @objectstack/hono test           → 2 files / 73 passed
npx eslint(本 PR 6 个文件)                     → 无输出
pnpm --filter @objectstack/spec check:generated → ✓ All 8 generated artifacts are up to date
check:nul-bytes / route-envelope / error-code-casing / wildcard-fallthrough / slot-lookup / role-word → 全绿
git diff origin/main --numstat                 → 7 个文件,无 `- -`(无二进制)

真实 boot 集成用例(dispatcher-plugin.endpoint-fallback.integration.test.ts,LiteKernel + 真实 Hono + 真实兜底器,走真实 socket;stub 只打在 kernel 服务边界上,socket 到 stub 之间全是生产路径):

  • object_operation find → 200,体是 /data 的体(success(result),{object, records, total}),经真实 action-execution.callData(无 protocol 服务 → ObjectQL 回退路径);
  • flow → automation 槽被调用,上下文是 buildAutomationContext 的形状({recordId, objectName, params} 翻译 + < objectName >Id 驼峰别名 + userId 身份);
  • authRequired 默认 → 匿名 401 UNAUTHENTICATED,带会话 → 200;
  • rateLimit → 第二次 429 且 Retry-After 在线上(这是本 PR 补的丢头回归的钉子:步一级的断言一直是绿的,只有 socket 上的断言能抓住它);
  • cacheTtl → 成功答复带 Cache-Control: private, max-age=30;同样声明 cacheTtl 但执行失败(get?id= → 400 VALIDATION_FAILED + fields[])的答复不带;
  • 未命中 / 前缀外 / 无 matcher / 已注册路由不被遮蔽 / 405 + Allow → E3 的否定断言逐条保持;
  • matchEndpoint 抛错 → 5xx 包络;
  • 组合运行时永不产出两个「没接线」501(逐条扫响应正文)。

范围外(已如实归档)


🤖 Generated with Claude Code

https://claude.ai/code/session_01EYGdmvWP1ieZSLqvAW6uyd

派发步命中分支的 501 换成完整链:匹配 → 策略(E4)→ executeEndpointTarget(E5)
→ 响应映射。cacheTtl 的 Cache-Control 只并进成功答复,错误答复一律不带。

兜底器补三根线:
- 每进程构建一次端点限流注册表(与 server 级限流器同一个 resolveCache);
- 每请求喂完整 EndpointPolicyContext(headers / remoteAddress /
  resolveSessionPrincipalId / limiters / trustProxy / logger),并把
  answer.headers 写到线上(429 的 Retry-After 此前被丢弃);
- 匹配前用 HttpDispatcher.resolveRequestScope(自 dispatch() 原地抽出)解析本
  请求的环境 / 身份 / driver,委派调用带调用方 ExecutionContext 运行。

多租户 host 解析不到环境时该步弃权(不写任何东西),不拿默认 kernel 作答。

现网行为零变更:非空 apis: 在 publish 仍被硬拒(E7 前不撤),整条链结构性不可达。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYGdmvWP1ieZSLqvAW6uyd
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 6:44am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/l labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/runtime.

21 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via packages/runtime)
  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/runtime)
  • content/docs/kernel/cluster.mdx (via @objectstack/runtime)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime)
  • content/docs/plugins/packages.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/implementation-status.mdx (via @objectstack/runtime)
  • content/docs/releases/v17.mdx (via @objectstack/runtime)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review August 4, 2026 06:45
@os-zhuang
os-zhuang enabled auto-merge August 4, 2026 06:45
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 81e2744 Aug 4, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5129-wire-endpoint-chain branch August 4, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants