Skip to content

test(client): close the route audit's reverse direction — every SDK URL must match a route some surface mounts (#3642) - #3658

Merged
os-zhuang merged 1 commit into
mainfrom
claude/route-audit-tranche-3-service-wsxqaf
Jul 27, 2026
Merged

test(client): close the route audit's reverse direction — every SDK URL must match a route some surface mounts (#3642)#3658
os-zhuang merged 1 commit into
mainfrom
claude/route-audit-tranche-3-service-wsxqaf

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #3642 —— #3563 路由审计的收口项。

前四个守卫都没问的那个问题

dispatcher(#3563)、REST(#3587)、服务自主挂载(#3636)三份账本走的都是 server → client:枚举某个表面挂了什么、要求每条有过审的处置、sdk 行要求那个客户端方法存在。

"方法存在"不等于"方法调得通"。 没有任何一个守卫拿客户端拼出的 URL 去比对各表面挂载的 pattern。所以一个方法可以指向真实函数、挂着绿色账本行,同时在所有表面上 404。

这个洞已经发作过四次,每次都是人肉撞见的:

方法 发的 URL 谁在服务 发现于
analytics.explain /explain #3584
analytics.meta /meta/:cube #3584
meta.getView ?type= 只挂 /ui/view/:object/:type #3611
i18n.getTranslations / getFieldLabels ?locale= 只挂 path 形式 #3636

最后那一对从第一批起就各挂着一行绿色的 sdk,整整三批都是线上 404。

做法:真跑,而不是声明表

client-url-conformance.test.ts 用一个会记录请求的 fetch 驱动客户端上的每一个方法,再把捕获到的 URL 去比对四份账本的并集(是并集不是交集——只被一个表面挂载的路由同样可达)。

没有采用"方法 X 打路由 Y"的手写声明表:那种表是关于代码的断言,代码可以从它底下漂走——而那正是本次要修的失败模式本身。真跑一遍、抓它实际放到线上的东西,漂不走。

做了变异测试:把 #3636 那个方言 bug 重新注进去,守卫立刻失败并精确点名 ——

i18n.getTranslations → GET /api/v1/i18n/translations

一个不会失败的守卫没有价值,所以这步是必须的。

扫描自身的完备性也被断言了

这是最容易烂掉、也是 issue 里点名"绝不能跳过"的部分:

断言 挡住什么
每个方法要么被驱动、要么声明 NON_HTTP 并给出理由 新增 SDK 方法悄悄逃出覆盖
被驱动却发出零个请求 → 失败 占位参数过期导致方法在 fetch 前就抛错,守卫全绿却什么都没覆盖
URL 里出现 undefined / [object Object] → 失败 占位参数被接受但其实是错的,冒充成覆盖
(unmatched) 排除出 pattern 集合 __api-endpoint 那条 catch-all 匹配一切,让整个套件变成空转

第二条不是假想:调试过程中 connectauth.signInWithProvider 就是这样静默的——前者是原型方法被按名取出后丢了 this,后者在 node 下卡在 window 守卫上。两个都是真会发 HTTP 的方法,靠这条断言才暴露出来。signInWithProvider 我给了浏览器桩而不是塞进 NON_HTTP——把一个真发请求的方法记为豁免,就是这个守卫要防的那种腐烂。

结果与两处如实标注的边界

~219 个方法中 196 个匹配成功。 未匹配的只有 projects.* 一族。

这两个边界我选择数出来写进断言,而不是让它们隐没在"全绿"里——否则这个守卫会显得比它实际的强度更强。

验证

  • client-url-conformance.test.ts 3 项通过;变异测试确认可失败
  • packages/client 全量 13 文件 175 项通过
  • turbo run test --filter=@objectstack/client --filter=@objectstack/runtime --filter=@objectstack/rest —— 29/29
  • eslint --no-inline-configcheck-doc-authoringcheck-nul-bytes 均通过

无运行时改动:一个守卫,加上账本头部与审计文档(新增 §10)对"它覆盖了什么、没覆盖什么"的记录。


Generated by Claude Code

…RL must match a route some surface mounts (#3642)

The capstone of the #3563 audit. The dispatcher (#3563), REST (#3587) and
service-mount (#3636) ledgers all run server → client: enumerate what a
surface mounts, demand a reviewed disposition, and for `sdk` rows demand
the named client method exists. None asked the reverse question — does
the URL the client BUILDS match anything a server MOUNTS? — so a method
could name a real function, carry a green ledger row, and 404 everywhere.

That shipped four times, found one at a time by hand: analytics.explain
and analytics.meta (#3584), meta.getView (#3611), and i18n.getTranslations
/ getFieldLabels (#3636) — the last pair having carried green `sdk` rows
since tranche 1.

client-url-conformance.test.ts drives every method on a real client with
a recording fetch and matches each captured URL against the UNION of all
four ledgers (a union, not an intersection — a route mounted by one
surface is still reachable). A real drive rather than a hand-written
"method X targets route Y" table: such a table is an assertion ABOUT the
code that the code can drift away from, which is the very failure being
fixed. Mutation-checked — re-injecting the #3636 dialect bug fails the
suite.

The sweep's own completeness is asserted, because that is the part that
rots silently:

  - every method is driven or declared NON_HTTP with a reason, so a new
    SDK method cannot escape coverage;
  - a driven method emitting ZERO requests fails — stale placeholder args
    are exactly how a sweep stops covering anything while staying green;
  - a URL containing undefined / [object Object] fails, so a placeholder
    that is accepted but wrong cannot masquerade as coverage;
  - the __api-endpoint `(unmatched)` catch-all is excluded from the
    pattern set, since treating it as a pattern would match every URL and
    make the suite vacuous.

196 of ~219 methods matched. Two bounds are reported rather than papered
over: /api/v1/cloud/* (23 projects.* methods) is served by the sibling
cloud repo — this repo's dispatcher explicitly refuses those paths — so
it is exempt by prefix and bounded from both ends, with a non-projects
method reaching it failing the suite (#3655); and 60 of ~196 matched
calls rest only on a `**` prefix claim rather than a resolvable route, 54
of them on `* /auth/**`, a count the guard ratchets so it can only shrink
(#3656).

No runtime change: a guard, plus the ledger-header and audit-doc notes
recording what it does and does not cover.

Refs #3642, #3563, #3587, #3636.

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

vercel Bot commented Jul 27, 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 Jul 27, 2026 1:14pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling and removed size/m labels Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/client, @objectstack/runtime.

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

  • content/docs/ai/skills-reference.mdx (via packages/client)
  • content/docs/api/client-sdk.mdx (via @objectstack/client, packages/runtime)
  • content/docs/api/data-flow.mdx (via @objectstack/client)
  • content/docs/api/environment-routing.mdx (via @objectstack/client)
  • content/docs/api/error-catalog.mdx (via @objectstack/client)
  • 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/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/client, @objectstack/runtime)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/client)
  • content/docs/kernel/runtime-services/index.mdx (via packages/client)
  • content/docs/permissions/authentication.mdx (via @objectstack/client, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime)
  • content/docs/plugins/packages.mdx (via @objectstack/client, @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/protocol/kernel/realtime-protocol.mdx (via @objectstack/client)
  • content/docs/releases/implementation-status.mdx (via @objectstack/client, @objectstack/runtime)
  • content/docs/releases/v16.mdx (via @objectstack/client)

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 July 27, 2026 13:26
@os-zhuang
os-zhuang merged commit 1b717e5 into main Jul 27, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/route-audit-tranche-3-service-wsxqaf branch July 27, 2026 13:26
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 tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Route audit capstone: a strict reverse guard — every client URL must match a route some surface mounts

2 participants