Skip to content

feat(spec,metadata-protocol,rest,client): 直挂面 packages / datasources 可发现,SDK 跟随通告的 base(#6633 · 路线 B) - #6712

Merged
os-project-manager merged 7 commits into
mainfrom
claude/issue-6633-sdk-follows-discovered-base
Aug 8, 2026
Merged

feat(spec,metadata-protocol,rest,client): 直挂面 packages / datasources 可发现,SDK 跟随通告的 base(#6633 · 路线 B)#6712
os-project-manager merged 7 commits into
mainfrom
claude/issue-6633-sdk-follows-discovered-base

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6633

按维护者 2026-08-08 裁定(路线 B,#6633 评论 5225249241;#6306 的前置卡)实施:方向 a + b + c 全取。跨席预派发声明已发且否决窗口开放(spec-surface 席 #6298 评论 5225247046;metadata 席 #6367 评论 5225247338),实施期间多次复读 #6633 评论,无任何席位异议。

缺陷(全部实测,origin/main = 78f0be872 复核;原始测量点 e39dd66e7)

  • 探针 C 案(mock fetch 驱动真实 ObjectStackClient):discovery 通告 routes.packages: '/backend/api/v9/packages' + routes.datasources: '/backend/api/v9/datasources' 时,packages.list 跟随(→ /backend/api/v9/packages),而 external.listTables 仍打 /api/v1/datasources/pg_main/external/tables —— client 五个 external.* 方法硬编码、无任何 discovery 机制。
  • rest 面从不通告 routes.packages:getDiscovery()(services 注册 package + external-datasource 时)routes 键实测仅 ['data','metadata'];registerDiscoveryEndpoints 只 override data/metadata/ui/mcp/auth。已挂载而不通告,即 ADR-0076 D12「advertise everything mounted」缺失的另一半。

四阶段(依赖序,每阶段一个 commit)

  1. spec(d61072d85):ApiRoutesSchema 新增 datasources 键(federation-admin 家族的 base)。注意:packages 键在 origin/main 已存在(discovery.zod.ts 既有声明),故 spec 阶段只需新增 datasources —— 与派发卡「gains a packages route key and a datasources route key」的表述相比,前者已成立,以现场为准。optional 语义同 mcp:缺席 = 未挂载。生成物(authorable-surface/api.jsonreferences/api/discovery.mdx)经 check:generated --fix 再生。
  2. metadata-protocol(37c1bd117):serviceToRouteKeypackage: 'packages';路由值经新增的 NON_SLOT_SERVICE_ROUTES 小循环流入(package 不是 CoreServiceName slot,进 SERVICE_CONFIG 会重演已退役的 graphql 缺陷并伪造 services 可用性条目)。datasources 在本 builder 刻意不通告——处置同 mcp(routes.mcp 是 REST /discovery 发出、objectui 真实消费、但 ApiRoutesSchema 从未声明的键(#4828 同族,低一层) #5679):federation 挂载属于 REST 宿主,本 builder 看不见;dispatcher 宿主根本没有 /datasources 域,通告即 D12 禁止的「通告未挂载」。conformance 测试钉住两个方向。
  3. rest(076f8365b):/discoveryroutes.packages / routes.datasources已录制直挂路由的投影(RestServer.getDirectMountRouteBases(),读的就是 registrar 挂载时迭代的那批数组,rest 的 9 条 direct-mount 路由对 RestServer 不可枚举 —— 因此进不了 /openapi.json,也进不了任何运行时自省 #5822)——通告与挂载同源派生,配置了 api.apiPath 时,rest 的 9 条 direct-mount 路由挂在 {basePath}/{version} 而不是 {apiPath} #6306 未来移 base 时通告按构造跟随,不需要再改这里。未挂载 ⇒ 不通告:无 package service 的 boot 会删掉 protocol 乐观通告的 packages 键。今天直挂 registrar 挂在 plugin 的 versionedBase(/api/v1)而非 getApiBasePath(),通告如实反映这一点(设 apiPath 的部署会通告 /api/v1/...——那正是路由真实所在;配置了 api.apiPath 时,rest 的 9 条 direct-mount 路由挂在 {basePath}/{version} 而不是 {apiPath} #6306 落地后两者一起移动)。
  4. client(aff5f3c94):五个 external.* 方法经 getRoute('datasources') 派生 base;未连接或服务端未通告时 fallback /api/v1/datasources,URL 与旧硬编码逐字节一致。routeMap 对声明键保持 TOTAL。ScopedProjectClientexternal.* 副本(已核对),无第二份需要改。

验收钉 —— mounted ⇒ advertised 奇偶钉

packages/rest/src/discovery-advertised-direct-mounts.parity.test.ts:用与生产完全相同的组合(RestServer.registerRoutes() + mountAndRecordDirectRoutes(...))boot 真实 rest 面到一张真实 handler 表,经该表读 /discovery,再断言通告的 routes.packages / routes.datasources URL 在同一张表里解析并应答。四个用例:默认 base、非默认 base(/backend/api/v9,载荷用例:证明通告不再从 convention 二次推导)、未挂载 ⇒ 不通告、scoped 挂载(environmentId 代入)。任何只动挂载或只动通告一侧的未来改动(含 #6306 移路由)在此必红——反向验证已实测(见下)。

client 侧三案:A(未连接 → /api/v1/... 逐字节不变,沿用既有 pin 测试)、B(已连接但无新键 → fallback 不变)、C(通告 rebased 键 → packages.* 与全部五个 external.* 一起跟随;五个断言合在一个用例,半修不可能保绿)。

反向验证(预测先行,逐向实测 —— 全部在 merge origin/main 之后的合并结果上重跑)

回退方式:只 git apply -R 我自己那一阶段对该文件的 diff,main 期间对同文件的改动保持在位,故测量不被 main 的改动混淆(protocol.tsrest-server.ts 在停机期间被 main 改过,变更区与我的区段无交叠)。每次测量后立即 git checkout HEAD -- 还原,收尾已核 worktree 干净。

回退预测实测
client 阶段(恢复硬编码,保留新测试)C 案红,首断言 listTables 收到 /api/v1/...;A/B 保绿(fallback 逐字节同,双向绿,不作为反向证据)✅ 1 failed / 158 passed,失败断言与预测逐字符一致
rest 阶段(恢复旧 rest-server.ts,保留奇偶钉)4/4 红,机理各异:默认 base 案红在 datasources undefined(packages 因 stage-2 的 /api/v1/packages 恰好同值——被掩盖的漂移);非默认 base 案红在 '/api/v1/packages' ≠ '/backend/api/v9/packages'(奇偶钉存在的意义);未挂载案红在 datasources;scoped 案红在未代入✅ 4/4 红,四条失败信息与预测逐一对应
metadata-protocol 阶段packages iff 测试红(undefined);datasources 阴性钉双向绿(方向钉,排除出反向证据);rest 奇偶钉在此回退下保绿(stage 3 从录制挂载投影,不读 protocol 通告)——stage 2 的独立红通道只有本包 conformance 测试,如实申报✅ 1 failed / 20 passed
spec 阶段(恢复 discovery.zod.ts)spec 自身 schema 测试红(datasources 被 strip → undefined);client typecheck 红(routeMap 多余属性)与 metadata-protocol declaredRouteKeys 红为预测未实测(避免为一次同义反复的 tsc 红做两轮全量 spec dist 重建;正向 typecheck 已在门禁跑)✅ 1 failed / 82 passed(实测半);另半预测未实测,如实申报

origin/main 的合并(d6d1a50be)

停机期间 main 前进约 3.7 小时。已 merge(无冲突),并按 AGENTS.md §9/§10/§11 处理:

  • 生成物 text-merge 陷阱实际发生:packages/spec/authorable-surface/api.json 被文本合并,本分支较旧的副本吃掉了 main 新增的 GetMetaItemLayeredResponse / GetMetaItemResponse 键。已按 §11 从合并后的源重建 spec 并再生该产物(46e82d44d),现分支相对 main 的 delta 恰为我意图中的一行 api/ApiRoutes:datasources;pre-commit 的 os-regen 标记也已确认清除。
  • 重叠复核:main 改过 protocol.ts / rest-server.ts,但变更区(rest ~2904/4354/4510,protocol ~28/1026/10163)与我的区段(rest ~3378 与 ~9200,protocol ~2768)无交叠,我的代码在合并后完好。
  • 合并前采集的绿一律作废,下节全部为合并结果上的重跑。

门禁(全部在合并结果上实跑)

  • turbo build:70 successful, 70 total
  • 四包测试全绿:spec 345 files / 8832 tests、metadata-protocol 58 / 629、rest 69 / 1043、client 21 / 271
  • pnpm lint(eslint 无输出)、typecheck-spectypecheck-client 全绿。@objectstack/rest@objectstack/metadata-protocol不声明 typecheck 脚本(二者是 DEBT 台账包),其 tsc 由 check:type-check-debt 复测——该门禁 GATE_OK,全仓无任何 DEBT/TEST_DEBT 上升(rest TEST_DEBT 实测 152 < 记录 163,只有信息性「可下调」行,按 AGENTS.md 不欠记账)。
  • lint.yml 两个作业的 check:* 全家共 39 项 GATE_OK / 0 GATE_FAIL,含 check:generated(10 项生成物全部 up to date)、check:route-envelopecheck:error-code-casingcheck:engine-double-contractcheck:nul-bytescheck:type-check-coveragecheck:adr-0087-registrationcheck:i18n{,-coverage}、新增的 check:kernel-hook-pairs 等。ratchet 的 baseline 均已对齐 d6d1a50

changeset 覆盖四包(spec/metadata-protocol/rest minor,client patch;无 breaking,故无需 ADR-0087 处置标记)。本 PR 带 changeset,不适用 skip-changeset 标签。

备注

  • ADR-0076 D12 文档本身(「advertise everything mounted」半句未成文)按派发卡要求不在本 PR 内改,已在报告中列为 docs 跟进建议。
  • PD chore: version packages #10 发现:client 还有一整族绕过 getRoute() 的硬编码面(email.send、约 20 条 cloud.environments.*ScopedProjectClient 的 scope 前缀)——单独立 issue(未认领),不在本卡四车道内。

🤖 Generated with Claude Code

https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx


Generated by Claude Code

… key (#6633 stage 1)
The base for the `datasources/:name/external/*` federation-admin family
(ADR-0015 §6.2). Optional like `mcp`: absent = not mounted (ADR-0076 D12).
`packages` already existed; only `datasources` is new. Generated artifacts
(authorable-surface/api.json, references/api/discovery.mdx) regenerated via
check:generated --fix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx
…f the package service is registered (#6633 stage 2)
serviceToRouteKey gains `package: 'packages'`; the route flows through a
NON_SLOT_SERVICE_ROUTES loop because `package` is not a CoreServiceName slot
(a non-slot SERVICE_CONFIG row is the retired graphql defect and would
fabricate a services entry). `datasources` deliberately stays un-advertised
here — same disposition as mcp (#5679): the federation mount belongs to the
REST host this builder cannot see. Conformance tests pin both directions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx
…s as projections of the recorded direct mounts (#6633 stage 3)
Mounted => advertised (ADR-0076 D12's unstated half): RestServer.
getDirectMountRouteBases() derives the advertised bases from the very route
arrays the direct-mount registrars iterated to mount (#5822) — one fact, two
consumers, so #6306's future mount-base move carries the advertisement with
it by construction. Not mounted => not advertised: a boot without the
package service deletes the protocol's optimistic packages entry. End-to-end
parity pin drives the composed surface at both /api/v1 and a non-default
base, plus the scoped mount.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx
…routes.datasources (#6633 stage 4)
The five federation-admin methods go through getRoute('datasources'):
connected clients follow the advertised base, unconnected (or unadvertising
servers) fall back to the /api/v1/datasources convention byte-identically.
routeMap stays total over the declared ApiRoutes keys. Cases B and C from
the issue pinned as tests — C asserts all five external.* URLs plus
packages.* in one case so a half-fix cannot stay green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx
…gin/main
The generated artifact text-merged (AGENTS.md §11): this branch's older copy
won the hunk main had grown (GetMetaItemLayeredResponse / GetMetaItemResponse
keys). Rebuilt from the MERGED source, so the artifact now carries both sides
and the branch delta vs main is exactly the one intended
`api/ApiRoutes:datasources` line. check:generated: all 10 up to date.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx
@vercel

vercelBot commented Aug 8, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 8, 2026 1:19pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/client, @objectstack/metadata-protocol, @objectstack/rest, @objectstack/spec.

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

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/connect-mcp.mdx(via @objectstack/rest)
  • content/docs/ai/skills-reference.mdx(via packages/client, @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/client, @objectstack/spec)
  • content/docs/api/data-flow.mdx(via @objectstack/client)
  • content/docs/api/environment-routing.mdx(via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/metadata-protocol, packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/client, @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via @objectstack/client, @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/client, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/metadata-protocol, @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authentication.mdx(via @objectstack/client, @objectstack/rest)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/client, @objectstack/rest, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/metadata-protocol, @objectstack/rest, @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via packages/rest, @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx(via @objectstack/client)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/client, @objectstack/rest, @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/client, @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/client, @objectstack/rest, @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/metadata-protocol, @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

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.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

2 participants

@os-project-manager@claude