Repository navigation
meta.saveItem's own docstring tells the caller to echo version back as an If-Match header — the SDK offers no way to send one, on either declaration #11713
Description
Activity
分诊(session
session_01LrqnyLhY3kYBBRird4peBT,2026-08-24):入决策箱(needs-user-decision+domain:cli,type Feature)。#11391 的裁决逐项列举了恰好三个 query 参数并明令「report rather than silently widen」—— 本卡是第四个载体(请求头),被刻意围栏在外,继承不适用,须单独裁。四棱卡面块如下。os-decision-facets
一句话问题:SDK 自己的文档告诉调用者「把
version用If-Match头带回来,就能防止两个人同时改一条元数据时互相覆盖」,但saveItem根本没有地方放这个头 —— 照文档做的人,并发编辑仍然静默互相覆盖,而不是收到 409 冲突提示。选项 × 真实代价:
- A 给
saveItem加ifMatch选项(放进 The first-party SDK'smeta.saveItemsends no query string at all, so the?force=truethe destructive 409 prescribes is unreachable through@objectstack/clienton either REST door #11391 已落地的SaveMetaItemOptions袋):文档说的能力真正可用;代价 = 已发布 SDK 公开面第四次加宽(超出 The first-party SDK'smeta.saveItemsends no query string at all, so the?force=truethe destructive 409 prescribes is unreachable through@objectstack/clienton either REST door #11391 裁定的三参数围栏),需同步两处声明 + headers 级 pin。 - B 删掉三处文档指引(承认 SDK 不支持,让要 OCC 的人走原始 HTTP):零新面;代价 = 第一方 SDK 永久缺一条服务端已经实现的数据保护路径,且同门另一个第一方客户端(
@object-ui/data-objectstack的MetadataClient)已经带ifMatch—— 两个第一方客户端能力不对齐。 - C 维持现状:文档继续教一个做不到的动作 —— 声明≠强制的活标本,不可取。
四棱:
① 实际业务拉动:并发覆盖是真实数据丢失场景;服务端If-Match→parentVersion已上线,同门客户端已实测需要它 —— 拉动是实的,不是投机面。
② 项目长远合理性:ADR-0008 乐观并发是平台既定契约;第一方 SDK 是该契约的正门,正门缺口比旁门(raw HTTP)长期更贵。A 与 #11391 的修复形状同族(同一个 options 袋),不是新形态。
③ 防 AI 犯错:今天的形态是最坏的 —— 文档声明了、类型系统不拒绝、运行时静默覆盖;AI 照文档生成的代码看起来对、实际丢数据。A 让声明=强制;B 至少消灭假声明。
④ 创业阶段不扩散:A 是一个可选参数进既有袋,边际极小;真正的扩散风险(query 参数枚举)已被 #11391 围栏,本项不破栏,只是栏外单独一票。推荐:A(
ifMatch进SaveMetaItemOptions,双声明同步,headers pin 补上);回退 B(同 PR 删三处指引,绝不留 C)。置信缺口:未测有多少外部消费者已在用 raw HTTP 绕行(A 不破坏他们);未测MetadataClient与本 SDK 的ifMatch语义是否逐字一致(实现时对齐)。实施 PR 属 Clause-②(扩大公开面)⇒ 契约复审档位。低摩擦裁决格式:回「A」/「B」即可;「A,但 X」按 A 落地并记 X。
Generated by Claude Code
- A 给
os-support-ai commented
on Aug 25, 2026 CollaboratorMore actionsMaintainer ruling — A:
ifMatchjoinsSaveMetaItemOptionsSource: maintainer, 2026-08-25, live PM chat (decision-inbox batch 3 review, session
session_01KWRU3s15AJz7PGW7a7wdCh), verbatim: 「同意」 — accepting the batch's presented recommendation for this card: A.Implementation shape, as presented and accepted:
ifMatchas an optional member of theSaveMetaItemOptionsbag The first-party SDK'smeta.saveItemsends no query string at all, so the?force=truethe destructive 409 prescribes is unreachable through@objectstack/clienton either REST door #11391 landed (same family as the two sibling methods atindex.ts:4689/4787), wired to theIf-Matchrequest header; bothsaveItemdeclarations stay in step.- Pins on the headers the method builds (the existing The first-party SDK's
meta.saveItemsends no query string at all, so the?force=truethe destructive 409 prescribes is unreachable through@objectstack/clienton either REST door #11391 pins measure the URL only). - Alignment check from the analysis's stated gap: verify the
ifMatchsemantics match@object-ui/data-objectstack'sMetadataClientwording exactly — two first-party clients, one behaviour. - C (docs keep teaching an impossible move) is dead either way; if implementation hits an unexpected wall, the fallback is B (remove the three doc pointers in the same PR), never C.
Clause-②: yes (widens the published SDK surface) — contract-review tier on the implementation.
State:
needs-user-decision→pm:queuein the same write;domain:clistands.
Generated by Claude Code
Measured while implementing #11391. Same defect shape as that card — the SDK prescribing something its own surface cannot do — one parameter over, so filed separately rather than folded: #11391's ruling enumerated exactly three query parameters (
force,packageId,mode) and this is a fourth carrier, a request header, which is a contract decision of its own.What was measured
The docstring directly above
meta.saveIteminpackages/client/src/index.tsreads:Neither
saveItemdeclaration accepts a header, anifMatchoption, or anything else that becomes one:Lines 696 / 939 / 1295 are the three places the SDK tells a caller to use
If-Match. Lines 4689–4792 are two other methods that actually implement it — so this is an absent option onsaveItem, not a client that cannot send the header.The server side is live: the REST
PUThandler reads it and threads it asparentVersion:So the ADR-0008 optimistic-concurrency protection the docstring describes is reachable over raw HTTP and unreachable through
@objectstack/client— a first-party SDK caller who follows the instruction has nowhere to put the token, and their concurrent edit silently overwrites instead of answering 409metadata_conflict. The sibling first-party clientMetadataClientin@object-ui/data-objectstackcarriesifMatchin its own save options for exactly this reason.Why it was not folded into #11391
That card's ruling names the three query parameters and the options bag that carries them.
If-Matchis a header rather than a query parameter, and adding it widens the published SDK surface a fourth time beyond what was ruled — so it is reported rather than taken. The bag landed by #11391 (SaveMetaItemOptions) is the natural place for it if that is ruled in.Where it would land
packages/client/src/index.ts— bothsaveItemdeclarations, which must stay in step — plus pins inpackages/client/src/client.test.tson the headers the methods build (the existing #11391 pins measure the URL only).Related: #11391 (the query-string half), and ADR-0008 for the OCC contract itself.
Measured on
origin/mainat4ceae8ab0.Generated by Claude Code
Generated by Claude Code