Skip to content

feat(rest,runtime): 元数据写入的其余三扇门同样要求 manage_metadata (#7019) - #7043

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-7019-meta-write-capability-gates
Aug 9, 2026
Merged

feat(rest,runtime): 元数据写入的其余三扇门同样要求 manage_metadata (#7019)#7043
os-project-manager merged 1 commit into
mainfrom
claude/issue-7019-meta-write-capability-gates

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #7019

#7027 门的 main(623d00897)开分支,三条肢体各自照抄所在文件的既有先例,不引入新写法。推送前已 git merge origin/main(至 08863dd18)。

0. 先更正卡里/评论里的两处坐标 —— 都自己实测过

说法 出处 实测
dispatcher 无门 PUT 在 meta.ts:264 卡正文 对的
:264 是错的,真正的写在 :152-159 PM 评论 5231054845 这条更正本身是错的

在 PM 指名的那个 commit 91cefb849 上(以及 c3f491626、当时的 main),packages/runtime/src唯一method === 'PUT' 就是 domains/meta.ts:264;:152-159 落在 maskObjectSchemaList / handleMetadataRequest 开头。结论的实质不变(dispatcher 确有一条无能力门的 PUT),错的只是那条更正。

meta.ts 既有 manage_metadata 门的坐标(两个互相矛盾的读数,卡里要求自己定位):落地前该文件共 602 行,门在 :464-476(_migrate-stored)。:331-339:460-476 都不对。

1. 三条肢体的 before / after

肢体 before after
PUT /meta/:type/:section/:name(复合名) resolveProtocol 后直落 saveMetaItem,无任何能力检查 门在 resolveProtocol 之前,403 {error:{code:'FORBIDDEN'}}
DELETE /meta/:type/:name 同形无门,?dropStorage=true 还会拆掉对象物理表 同上,message 为 Resetting a metadata item requires ...
dispatcher PUT(runtime/src/domains/meta.ts) if (method === 'PUT' && body) 直接 resolveService('protocol')saveMetaItem 门在解析 protocol 之前,deps.error(msg, 403)

两个包的 403 code 不同,是刻意的:REST 侧照抄 _migrate-stored#7027,写死 FORBIDDEN;dispatcher 侧照抄同文件_migrate-stored 门,走 deps.error(msg, 403),code 由 standardErrorCodeForHttpStatus(403) 派生为 PERMISSION_DENIED。统一两种拼写是契约变更,不属于本卡。

2. 真红:预测先写后跑

动手前写下的预测(方向 RED),与实测对照:

预测 实测 对否
dispatcher 门 → http-dispatcher.test.ts 约 8 例转红 正好 8 例,行号也对上
复合名 PUT → 无既有测试转红(仓内无人驱动该路由) 确实 0 例
DELETE → 无既有测试转红,但只因为 #7027 先落地(它已给 rest-5xx-message-sanitization.test.tsmountRest 补了 manage_metadata) 确实 0 例,该文件我一行没碰
只有 http-dispatcher.test.ts 一个文件受连带 预测漏了 2 个文件 / 6 例

预测错的那一处,如实记下:我用 grep "'PUT'" 扫测试文件来估连带面,漏掉了两个经 dispatcher.dispatch(...)(而非 handleMetadata)驱动的文件 —— http-dispatcher.error-leak.test.ts(4 例)与 domains/error-passthrough.test.ts(2 例)。合计连带 14 例 / 3 文件,不是预测的 8 例 / 1 文件。

反向验证(删门重跑),方向与预测一致

  • packages/rest 新钉:18 例中 10 例转红
  • packages/runtime 新钉:9 例中 5 例转红

没转红的那些是护栏而非证据,按要求明说:isSystem 旁路、持 manage_metadata 放行、匿名 401、读路径不受影响 —— 这几例加门前后都绿。

缺陷本身的前后实测(不是只看状态码)

复现 PM 量到的那条链路(真实路由 + 真实内存库,受限调用方 {userId:'u_portal', systemPermissions:[]}):

                      加门前                       加门后
compound GET status : 200                          200
compound GET fields : id, name                     id, name
STORE before PUT    : bonus_formula, id, name,     bonus_formula, id, name,
                      salary_grade                 salary_grade
compound PUT status : 200                          403
saveMetaItem calls  : 1                            0
STORE after PUT     : id, name        ← 两字段被删   bonus_formula, id, name, salary_grade

3. 钉的是「没进去」,不只是状态码

每条肢体都断言 status + code(ADR-0112 信封)目标函数一次没进(spy),并直接读库:

  • 复合名 PUT:saveMetaItem 零调用 + 库中四字段完好 + label 未变;
  • DELETE:deleteMetaItem 零调用 + 覆盖层仍在,?dropStorage=true 变体单独一例 ——「先删了再 403」正是最值得防的失败形态,只断状态码看不见;
  • dispatcher:saveMetaItem 零调用 + 库不变;另钉 MetadataService 回退分支(门必须在分支之前,否则第二个 writer 仍无门)与复合名形式。

这些路由是发送而非抛出,toThrow 形状的断言在这里分不清「拒绝但信封错」与「根本没拒绝」。

4. 连带修改的 3 个既有测试文件 —— 为什么是必然连带

#7027 第 5.1 节同一类:这些用例的 boot 桩把「只要有 session 就能写元数据」当作既成前提,而这正是本 PR 要消灭的东西。门一落,请求在到达被测机制之前就被 403 拦住。

文件 例数 只改了什么
http-dispatcher.test.ts 8 新增 METADATA_AUTHOR()(与该文件既有 AUTHED_CALLER 同款,#5519 立的先例),只替换驱动 PUT 的 8 处;GET 用例一处未动
http-dispatcher.error-leak.test.ts 4 makeDispatcher 里覆写 timedResolveExecutionContext
domains/error-passthrough.test.ts 2 putMetaViaMetadataService 里同上

后两个文件走 dispatch(),而 dispatch()重新解析执行上下文(覆盖调用方传入的),这两个桩没有 objectql,解析出的调用方不持任何能力 —— 实测确认传入 systemPermissions 无效。覆写 timedResolveExecutionContext 是这个类在 dispatcher 侧的写法,对应 REST 套件用的 (rest as any).resolveExecCtx = ...被测机制、断言、期望值一字未动。

5. 改了什么

  • packages/rest/src/rest-server.ts —— 复合名 PUT + DELETE 两处门
  • packages/runtime/src/domains/meta.ts —— dispatcher PUT 的门
  • packages/rest/src/meta-compound-save-and-reset-capability-gate.test.ts —— 新建,18 例
  • packages/runtime/src/domains/meta-save-capability-gate.test.ts —— 新建,9 例
  • packages/rest/src/rest-route-ledger.ts —— 两行补 note,记法与 feat(rest): PUT /meta/:type/:name 要求 manage_metadata 能力 (#6603) #7027 一致
  • .changeset/meta-write-doors-manage-metadata-gate.md —— minor × 2 包
  • 上述 3 个既有测试文件的 boot 桩

6. 本地验证

  • pnpm --filter @objectstack/rest test76 文件 / 1187 例全绿
  • pnpm --filter @objectstack/runtime test116 文件 / 1753 例全绿(合并 main 后复跑)
  • pnpm --filter @objectstack/rest --filter @objectstack/runtime typecheck → 通过
  • check:route-envelope / check:error-code-casing / check:empty-changeset / check:authz-resolver / check:adr-anchors / check:meta-type-normalized / check:nul-bytes → 全部 PASS
  • 改动文件 eslint --no-inline-config → 无输出

7. 不属于本卡(未捎带)

三个相邻 handler 的错误信封三形状({error:{code,message}} / 裸 {error:'...'} / {error:'...',code:'...'} 兄弟键)按派单要求一律未改 —— 我新加的 403 一律用正确的嵌套形状,但既有的 501 出口原样保留。

#7020 记录的「门要求的能力集 ≠ ADR-0106 D4 掩码豁免集」仍然成立,本 PR 不替维护者选对齐方向。


Generated by Claude Code

#6603 只给 PUT /api/v1/meta/:type/:name 落了 manage_metadata 门,同一个写
操作还有三扇门没有门。本次补齐,各自照抄所在文件的既有先例,不引入新写法:

- PUT /api/v1/meta/:type/:section/:name(复合名保存)
- DELETE /api/v1/meta/:type/:name(重置为构件默认值)
- 运行时 dispatcher 自己的 /meta PUT(同一操作的第二条传输)

三处门都落在解析 protocol 之前,使未授权调用方无法用 501-vs-200 指纹探测
内核能力,且拒绝时什么都没写、什么都没删。isSystem 照例旁路。

复合名那扇门的前后实测(同一往返、真实内存库):
  加门前 PUT 200 / saveMetaItem 1 次 / 库中只剩 id,name
  加门后 PUT 403 / saveMetaItem 0 次 / 库中四个字段完好

DELETE 那扇门是另一条理由:不往返、不掩码,只是把定制覆盖层整个丢掉。

连带修改 3 个既有测试文件的 boot 桩(补 manage_metadata):它们的桩把
「只要有 session 就能写元数据」当作既成前提,而这正是本次要消灭的东西。
被测机制、断言、期望值一字未动。

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

vercel Bot commented Aug 9, 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 9, 2026 11:30am

Request Review

@github-actions github-actions Bot added the size/l label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/connect-mcp.mdx (via @objectstack/rest)
  • content/docs/api/client-sdk.mdx (via packages/runtime)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest, @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/rest, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime)
  • content/docs/permissions/system-context.mdx (via packages/rest, packages/runtime)
  • content/docs/plugins/index.mdx (via @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/rest, @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/rest, @objectstack/runtime)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/rest)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)

3 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/rest, @objectstack/runtime)
  • content/docs/releases/v12.mdx (via @objectstack/rest)
  • content/docs/releases/v17.mdx (via @objectstack/rest, @objectstack/runtime)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 9, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 9, 2026 11:43
@os-project-manager
os-project-manager added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit 73648ba Aug 9, 2026
26 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7019-meta-write-capability-gates branch August 9, 2026 11: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

2 participants