docs(spec): 补齐 preserveAudit 的 .describe(),并按实测形态重写 RLS using 的描述 (#6881, #6762) - #6918
Conversation
, #6762) 两条同类的 spec-surface sweep:已发布的 `.describe()` 说明不足。属性上方的 TSDoc 块不进生成器,只有 `.describe()` 会进 `content/docs/references/**`, 所以这两处直接决定参考文档里那一格渲染出什么。纯文本改动 —— 没有 key、 类型、枚举成员、refinement 或默认值变动,acceptance surface 逐字节不变 (authorable-surface/、json-schema.manifest/、authorable-defaults/ 三者 gen:schema 后零 diff)。 #6881 —— ExecutionContextSchema.preserveAudit 此前是裸声明,语义只写在上方块注释里,于是 references/kernel/ execution-context.mdx:69 的描述列渲染为空。现在补上描述,并按 #6640 收窄 后的契约措辞,与已合入的两处(FieldSchema.readonly、protocol/objectql/ security.mdx 的 callout,均出自 PR #6823)保持同一口径:豁免仅在 UPDATE 路径成立;INSERT 侧在 DataProtocol ingress 更早剥离,只认 context.isSystem, 非 system 的 create 即便携带 preserveAudit 仍被剥离并记 WARN。 #6762 —— RowLevelSecurityPolicySchema.using 此前宣称「四种编译器支持的形式之一」且四种拼写全是 SQL 方言,两个方向都不准: 实测 isSupportedRlsExpression 之下 `!=`/`<`/`<=`/`>`/`>=`、对内联字面量列表 的 `in`、`&&`、`||` 与裸 `true` 都真正生效(比宣称的宽);而 ADR-0058 D1 定 CEL 为规范方言、sqlPredicateToCel 标 @deprecated(指向正在退役的方言)。 改为按能被下推的形态描述 —— 不重新数一个固定数目,换一个同样错的计数是同一 个缺陷 —— 并把 SQL 拼写降格为过渡桥接(`=`→`==`、`IN`→`in` 仍接受;SQL 的 AND/OR/NOT IN/IS NULL/LIKE 不在桥接内,fail-closed)。 两处各加了一条读回 description 的 pin,按 idiom 而非逐字断言(改写自由, 丢失实质不自由),并各带一条 anti-vacuity 非空断言。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 113 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
PR #6918 修好了 `using` 的 `.describe()`,但同属性上方 45 行处的 TSDoc 块 (`:313`「Exactly four forms compile」)仍是旧说法。改之前两处一致地错,改 之后两处互相矛盾 —— 而读源码的人(常常是 AI,ADR-0033)先撞上的是那段更长、 更像权威的语法规范,没有任何信号提示下方那一行 `.describe()` 才是当前事实。 这里只加标记,不重写:重写那 60 行语法规范散文(连同 5 条 SQL 方言的 `@example`)是 #6919 自己的卡,塞进这张两条目 sweep 卡会破坏其 per-item 复核模型。标记把「两条互相矛盾的断言」降级为「一条断言 + 一句诚实警告」, 直到真正的修复落地。 该块不进生成器(gen:docs 只渲染 `.describe()` 与模块级 docblock),已实测 确认:加这段标记后 content/docs/references/** 零 diff。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk
PM 复核裁定:该行与属性级 TSDoc 性质不同,应并入本 PR。 `packages/spec/src/security/rls.zod.ts:79` 的 「A small, fixed expression grammar (equality, set-membership, always-true)」 是**模块级** docblock,会逐字渲染到 content/docs/references/security/rls.mdx:79 —— 与属性级 TSDoc(不进生成器,留给 #6919)不同,它是已发布面。 不改的话,本 PR 合入后同一张渲染页会自相矛盾:`:79` 说三项,`:174` 是按实测 改正的 using 行,而读者先撞上 `:79`。 改法与 `.describe()` 同一口径:讲能被下推的形态、canonical CEL,⛔ 不换一个 新的固定计数(「三」换「八」是同一个缺陷)。实测依据:比较算子全套、对 `current_user.*` 数组与内联字面量列表的 `in`、`&&`/`||` 均真正下推;其余 fail-closed。 现在 rls.mdx 相对 origin/main 恰好两行变化,且页内无残留旧说法。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31297338477 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
队列失败分诊 —— 与本 PR 无关,⛔ 暂不重排。完整分析在 #6935 的同类评论。 简述,因为这两张是同一件事的两个观测点: 本 PR 只改 决定性的一点:本席的另一张 PR #6935 在一次独立的队列构建里红在完全相同的四条断言上(本 PR build 31297338477,#6935 是 31297316580)。两张 PR 文件面完全不同(#6935 折的是 两次独立构建、同一组失败、两个无关的 diff ⇒ 排除「某一张 PR 造成的回归」。 因此不重排: 两张都中,说明触发条件与 diff 无关,重排只会再烧一轮全队列然后红在同一处。对照今晚的 #6847 —— 那次重排一次即过,但判据是同批的 #6846 跑同一套全量套件并合并成功,存在绿的对照;这次没有。 本席正在独立探针里跑该钉在干净 main 上的结论,以区分「main 已坏、影响所有排队 PR」与「同批语义冲突」。拿到读数前不动,拿到后若是前者会立卡并通知维护者(归 Generated by Claude Code |
Fixes #6881
Fixes #6762
Paired 「spec-surface sweep」卡(#6243 模型) —— 两条同类问题:已发布的
.describe()说明不足。属性上方的 TSDoc 块不进生成器,只有
.describe()会进content/docs/references/**,所以这两处直接决定参考文档里那一格渲染出什么。按条目 checklist 复核,两条彼此独立、可分别裁决。
条目 1 —— #6881:
ExecutionContextSchema.preserveAudit没有.describe()锚点(实测):
packages/spec/src/kernel/execution-context.zod.ts:356(卡上写 ~:356,实测一致)preserveAudit: z.boolean().optional(),,语义只写在上方:329-355的块注释里,于是content/docs/references/kernel/execution-context.mdx:69渲染成| **preserveAudit** | boolean | optional | |,描述列为空。.describe(),参考行不再空白;措辞按 [观察]stripReadonlyForInsert完全不读preserveAudit——readonly的历史导入豁免在 INSERT 侧是 declared ≠ enforced #6640 收窄后的契约。口径对齐的两处已合入站点(读过,不另造第三种拼法):
packages/spec/src/data/field.zod.ts—FieldSchema.readonly(PR #6823)readonly字段content/docs/protocol/objectql/security.mdx的 callout(同一 PR #6823)context.isSystem」、「the server logs aWARN… the strip still applies」即落到描述里的规则:豁免仅在 UPDATE 路径成立;INSERT 侧只认
context.isSystem,非 system 的 create 请求即便携带preserveAudit,字段仍被剥离并记 WARN。条目 2 —— #6762:
rls.zod.tsusing的.describe()既 under-promise 又指向正在退役的方言锚点(实测):
packages/spec/src/security/rls.zod.ts:358(打标记后现为:364)field = current_user.< prop >,field = 'literal',field IN (current_user.< array >), or1 = 1.」两个方向都不准:比实际编译的窄,且四种拼写全是 SQL 方言(ADR-0058 D1 定其为过渡态,sqlPredicateToCel标@deprecated)。须实测:门实际接受什么(不照抄卡上的清单)
我自己跑了
isSupportedRlsExpression,实测 ENFORCES(远宽于「四种」):==对上下文值 / 字面量organization_id == current_user.organization_id/status == 'published'!=region != null<<=>>=amount > 100、amount <= 100in对current_user.*数组assigned_to_id in current_user.team_member_idsin对内联字面量列表status in ['draft', 'pending']&&/||a == 1 && b == 2、a == 1 || b == 2true=→==、IN→in、1 = 1实测 FAILS CLOSED:SQL 的
AND/OR/NOT IN/IS NULL/LIKE、算术amount + 1 > 2、子查询、跨对象record.a.b == 1、裸真值字段is_active、取反!is_active、空串。||与内联字面量列表这两项是本次实测的增量发现(issue 正文只提到&&),已写进描述。CEL 拼写与同文件 PR #6729 落下的
check@example(status in ['draft', 'pending'])一致。Lane admission —— acceptance 逐字节不变
domain:spec-surface。.describe()不参与 parsing;无任何 key / 类型 / 枚举成员 / refinement / 默认值移动。证据:gen:schema之后(
packages/spec/json-schema/是.gitignore:61的构建产物,不进版本库;已确认其磁盘内容确实随本次改动重生成。)生成产物(两条同乘一次重生成)
pnpm --filter @objectstack/spec gen:docs后恰好两个受版本控制的文件变化,各一行,无附带 churn:均为生成器产出,未手改;
content/docs/releases/未触碰。验证 —— 方向在跑之前先预测
两条都是正向内容断言,pin 读回 schema 上的
description并断言其必须承载的实质。git checkout origin/main --两个 zod 文件)''失败(正是「空单元格」缺陷本身);条目 2 的 6 条对旧 four-forms 串失败,含「不得再出现固定计数」那条抓到字面量four。.describe('')''上空洞通过,这正是非空臂存在的理由。按 idiom 而非逐字断言:改写自由(用
toMatch交替式),丢失实质不自由。pin 覆盖检索时同时搜了转义正则拼法(
toMatch(/…/))而不只字面量(#6854 的教训)—— 两处此前均无既有 pin。门的行为半边不重复 pin:
packages/formula/src/rls-predicate.test.ts:46-67已经钉住isSupportedRlsExpression的红/绿线,本 PR 只钉「散文与之相符」,避免同一事实两处钉。结果
pnpm --filter @objectstack/spec test→ 348 files / 8967 tests passed(合入origin/main之后、以及加标记之后各复跑一次)pnpm --filter @objectstack/spec typecheck→ 通过pnpm lint、check:generated、check:docs、check:authorable-surface、check:api-surface、check:spec-changes、check:upgrade-guide、check:skill-refs、check:doc-authoring、check:docs-audit-scope、check:adr-anchors、check:nul-bytes→ 全部 exit 0test-typecheck-debt.json账本(@ts-expect-error退役 pin 在packages/spec里是幽灵检查:tsconfig 把**/*.test.ts排除出唯一的tsc --noEmit#5286 ratchet):前后均为 58 file(s) / 266 error(s),文件 sha256b5e1c38e…逐字节未变 —— 两处新 pin 不带 tsc 错误。范围 —— 刻意留在范围外的相邻问题,已立卡 #6919
⛔ 只有这两条修复,无搭车。相邻问题已作为正式 GitHub issue 立卡:#6919(未指派;
finding+domain:spec-surface,无pm:queue—— 观察类归档,不进派发池)。不是 PR 评论:合入后评论就不再是任何人会看的地方。#6919 记录的是同一句话的两处副本,其中一处是我在复核期间新实测到的,且已发布:
rls.zod.ts属性级 TSDoc(:296-354)gen:docs只渲染.describe()与模块级 docblock)=以外的比较算子」rls.zod.ts:79模块级 docblockcontent/docs/references/security/rls.mdx:79第二行是本 PR 没有动、但值得分诊优先看的一处:合入后
rls.mdx同一张渲染页上会同时有:79的旧三项说法和:174已改正的using行 —— 自相矛盾落在已发布面上。之所以仍未动它:派发卡把条目 2 限定为.describe()字符串,改它会额外改动一个已发布页面、超出本 PR「恰好两文件各一行」的证据面。按纪律记录、交分诊,不自行扩面。本 PR 就近做了一件事:给 TSDoc 那段打
⚠️ STALE标记,不是重写。 改之前两处一致地错,改之后会互相矛盾 —— 而读源码者(常是 AI,ADR-0033)先撞上的是那段更长、更像权威的语法规范。标记把「两条互相矛盾的断言」降级为「一条断言 + 一句诚实警告」,指向 #6919,一行成本,不破坏 per-item 复核模型。重写那 60 行语法规范散文(连同 5 条 SQL 方言@example)是 #6919 自己的卡。实测确认标记不进生成器:加入后
content/docs/references/**零 diff —— 这也再次印证了 TSDoc 那半是观察类。Changeset:
.changeset/describe-preserve-audit-and-rls-using.md(@objectstack/spec: patch)。🤖 Generated with Claude Code
https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk