观察类发现,来自 #5588(PR #5821)的实现过程,按 Prime Directive #10 单独立单。**今天没有用户会撞到**:它只影响文档覆盖面,不影响任何路由的可用性。 ## 事实 `packages/rest` 有两个 registrar 绕过 `RouteManager`、直接注册到 `IHttpServer`: - `packages/rest/src/package-routes.ts` —— `POST /api/v1/packages/publish`、`GET /api/v1/packages`、`GET /api/v1/packages/:id`、`DELETE /api/v1/packages/:id` - `packages/rest/src/external-datasource-routes.ts` —— `datasources/:name/external/*` 5 条 台账 `packages/rest/src/rest-route-ledger.ts` 里它们是 `source: 'direct-mount'` 的 9 行,其中 8 行 `disposition: 'sdk'`(`packages.list` / `packages.get` / `packages.uninstall` / `datasources.external.*` 都是 SDK 表达得出来的真实能力)。 由 `rest-api-plugin.ts` 分别在 `packageService` / external-datasource 服务存在时调用,`RestServer` 自身**不持有任何记录**:`RestServer.getRoutes()`(= `routeManager.getAll()`)看不到它们。 ## 后果 #5588 裁定 C 之后,`GET {apiPath}/openapi.json` 的 built-in 段由 `routeManager.getAll()` 产出(PR #5821)。这 9 条因此**不在文档里** —— 这是 #5821 刻意选的边界并写进了 PR 正文:本服务器不持有「本次 boot 它们是否挂载」的事实,凭空补上就是 #5588 修的那类幽灵。所以现状是 **honest but incomplete**,不是 bug: - 拿 `/openapi.json` 生成客户端的 consumer 拿不到 `packages.*` 与 `datasources.external.*` 这 9 条(其中 8 条 SDK 有对应方法),要自己照台账手写; - 除 openapi 之外,任何基于 `getRoutes()` 的自省(调试、路由清单、未来的门禁)同样看不见它们;`rest-route-ledger.conformance.test.ts` 之所以能守住它们,是因为它**另起一套**枚举方式(用 mock server 捕获注册调用),而不是问服务器。 ## 可能的处置(留给分诊,不预设) 1. 让两个 registrar 返回自己挂载的路由描述,由 `rest-api-plugin.ts` 交给 `RestServer` 登记为「已挂载」—— 事实来自实际调用点,不新增第二真相源; 2. 或者把它们迁到 `RouteManager` 注册(需确认当初绕开是否有原因 —— `package-routes.ts` 的注释只解释了为什么不用 `POST /packages`,没解释为什么绕开 RouteManager); 3. 或者维持现状,把「文档不含 direct-mount」明确为契约。 处置口径上 (1) 与 (2) 都会顺带让 `rest-route-ledger.conformance.test.ts` 的两套枚举收敛成一套。 --- _Generated by [Claude Code](https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ)_
Activity
os-zhuang commentedon Aug 6, 2026
发现分诊:持有(维持
finding) + 补domain:cli。域标签理由:落点在
packages/rest(package-routes.ts/external-datasource-routes.ts/rest-server.ts/rest-route-ledger.ts),packages/rest在 SKILL 域表 cli 行。与产生它的 #5588 同车道,处置时不必跨座位。过时前提检查(
origin/main@846ed1f,实读,成立):rest-route-ledger.ts的direct-mount命中 13 处 ✅(正文说的 9 条路由 + 相关注记),台账仍如实记着这批路由;rest-server.ts:8457-8458getRoutes() { return this.routeManager.getAll(); }✅ —— 即RestServer确实只能看见RouteManager里的东西,direct-mount 的 9 条对它不可枚举,前提原样成立。判持有(而非晋级)的理由 —— 两条,第二条是硬的:
/api/v1/openapi.json描述的 built-in 路由一条都不存在 —— 10 个 operation 真实 boot 全部 404(证伪 #5456 的「当前未漂移」) #5588 / PR fix(rest):/openapi.json的 built-in 路由段改由 rest 按自身路由事实产出(#5588,裁定 C 第一棒) #5821 改写 —— 本单描述的现状(「built-in 段由routeManager.getAll()产出,因此这 9 条不在文档里」)正是 fix(rest):/openapi.json的 built-in 路由段改由 rest 按自身路由事实产出(#5588,裁定 C 第一棒) #5821 刚刚选定的边界,而该 PR 还没落地。现在晋级派发,dev 会在一个尚未稳定的边界上做决策;更糟的是三个处置选项里,(1)(2) 都要改 fix(rest):/openapi.json的 built-in 路由段改由 rest 按自身路由事实产出(#5588,裁定 C 第一棒) #5821 正在动的那条枚举路径,是直接的同文件撞车。⛔ 重启条件(明确、可机械判定):#5588 / PR #5821 合入
main之后。那一刻/openapi.json的 built-in 段成为既成事实,三个选项的成本才是可比的,由 cli 座位判级。判级时的提示(供届时参考,不构成裁决):三个选项不是平权的 —— (3)「维持现状并把『文档不含 direct-mount』明确为契约」需要回答一个不便宜的问题:8/9 条的⚠️ 没查清这条就改成 RouteManager 注册,是在拆一个可能有原因的绕行。
disposition是sdk,即 SDK 已经表达得出来的真实能力,把它们永久排除在生成的客户端之外,是把「服务器不持有该事实」这个实现约束升格成对外契约。(1) 看起来最省(事实来自实际调用点,不新增第二真相源,且顺带让rest-route-ledger.conformance.test.ts现在那套「用 mock server 捕获注册调用」的第二套枚举与主枚举收敛成一套)。(2) 之前需先答正文点出的那个未解问题:当初为什么绕开RouteManager——package-routes.ts的注释只解释了为什么不用POST /packages,没解释绕开的理由;查重:三仓 open issue / PR 各搜一遍(
direct-mount/getRoutes/openapi.json覆盖面 /rest-route-ledger)。#5588(在飞,PR #5821)是产生本单的现场、其 PR 正文已把本边界写明,非重复;#5563(rest 线全仓翻转)是相邻但不同现场。无影子单,不关重复。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
发现分诊(存量裁决轮,维护者 2026-08-06 委托,session_01LeEfA7CFwbJb7JJmXm2KM3):晋级
finding→pm:queue。分诊席的机械重启条件(PR #5821 合入)已于 07:05Z 触发——/openapi.json 的 built-in 段成为既成事实,而 9 条 direct-mount 路由(含 8 条 disposition=sdk 真实能力)对 RestServer 仍不可枚举(rest-api-plugin.ts:319-344 直挂 IHttpServer,getRoutes 只回 routeManager)。默认方向按席位提示选项 1(registrar 返回挂载描述、事实来自实际调用点,顺带收敛 conformance 测试第二套枚举);选项 2 须先答清当初为何绕开 RouteManager。Generated by Claude Code