Skip to content

fix(objectql): 让开门广告停止在它不成立的地方发声(#3438 收尾) - #4284

Merged
os-zhuang merged 1 commit into
mainfrom
claude/d1-d2-strict-default-flip-vbly5z
Jul 31, 2026
Merged

fix(objectql): 让开门广告停止在它不成立的地方发声(#3438 收尾)#4284
os-zhuang merged 1 commit into
mainfrom
claude/d1-d2-strict-default-flip-vbly5z

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

范围已经变了:广告本身被 #4253 抢先落地,本 PR 缩为修它的两个缺陷

这个 PR 原本做的是 ADR-0104 rollout 要求的两处广告位。开着的时候 #4253 把同一件事合进了 main3ec8186cb),os migrate meta 的收尾列表和 boot 那一行都已经在了,而且它挂在 kernel:bootstrapped 而非 ready 路径上——刻意避开 storage ready 的竞态,比我原来的位置对。我的那份实现已全部撤掉,改用 main 的。

留下的是两个真实缺陷,都在 #4253 刚落地的那行广告上,且都无条件发生在生产里。

一、那行广告在每一个部署上都响

objectHasCoveredValueField——本应让「没有被覆盖字段的对象不必付出标记查询」的休眠短路——按原始类型判断,而真实 registry 会给它注册的每个对象注入被覆盖类型的字段:organization_idowner_id(都是 system)、created_byupdated_by(都在 SKIP_FIELDS),四个 lookupvalidateRecord 在到达值形态检查之前把它们逐个跳过,于是这个短路对存在的每一个对象答 true,从未生效,它的 WeakMap 缓存的是一个常量。

改为使用校验器自己的 isScannableValueShapeField——扫描器早就 import 的那个谓词。「什么算被覆盖字段」的三份读法差一个子句,正是一个门禁最终去治理无人执行的字段的方式,也正是 #4235 自己论证过要防的形状。

二、那行广告在环境开关已经定调时说假话

广告完全不看环境变量,而它所报告的两个姿态都在部署标记之前短路:

  • OS_DATA_VALUE_SHAPE_STRICT_ENABLED=1VALUE_SHAPE_STRICT() 让两个 *StrictEffective 直接返回 strict。此时「checked but NOT enforced here」是假话
  • OS_ALLOW_LAX_VALUE_SHAPES=1 / OS_ALLOW_LAX_MEDIA_VALUES=1 → 运维主动选了宽松,跑那个迁移不会改变他得到的东西,指着它就是噪音。

两个门禁各查自己那一对(mediaPostureSetByEnv / valueShapePostureSetByEnv),因为两个 opt-out 是分类的、而 opt-in 同时打开两类;做成兄弟函数的理由与 mediaStrictEffective / valueShapeStrictEffective 是兄弟一致。判定按代价从低到高,没什么可播报的内核仍然到不了标记查询。

为什么 #4253 自己的测试抓不到这两个

engine.test.tsvi.mock('./registry')——mock 掉的 registry 只会把测试自己写的字段交给引擎,那四个注入的 lookup 根本不存在,环境开关也没有被覆盖。所以这些用例放在独立文件里,走真实 registerApp → registry 路径,这是测试能看见部署所看见之物的唯一方式。

回归守卫已取证:把谓词改回原始类型写法,stays silent when no object declares a covered field 立刻转红(只声明 text/textarea 的 note 照样触发广告);改回来即绿。#4253 的 125 条测试在本 PR 下全部照常通过。

执行语义没有任何变化:同样的门禁、同样的开关、同样的默认值。标记读取本就按进程记忆化,所以这修正的是 ADR 声明的性质,而非用户可见的开销。

改动范围

相对 main 只有 5 个文件:

文件 内容
packages/objectql/src/engine.ts 谓词修正 + 两个环境守卫(+46 −11)
packages/objectql/src/validation/record-validator.ts 导出 valueShapePostureSetByEnv / mediaPostureSetByEnv(+35)
packages/objectql/src/value-shape-scan-advisory.test.ts 真实 registry 下的 7 条用例(新增)
.changeset/adr-0104-gate-announcement-tells-the-truth.md patch changeset(新增)
content/docs/releases/v17.mdx 补上这行广告的适用范围(+15)

验证

全量 workspace 构建后:

套件 结果
@objectstack/objectql 1345 passed(含新增 7 条)
@objectstack/cli 628 passed
spec check:generated 8/8 全 PASS
全量构建 71 包(含 DTS) PASS
check:release-notes / check:doc-authoring PASS
ESLint(全部改动文件) 干净

关联

#3438 的工作项 2 尾部。广告位本体由 #4253 落地,本 PR 修它的两个缺陷;工作项 1(D2 翻转)与工作项 3(新库创建时记账)已分别落地。本 PR 合入后 #3438 可关闭。

一处留给维护者定夺:ADR-0104 第 826–833 行仍写着 os migrate meta 会显示门禁状态,而 #4253 有意没这么做(该命令不开数据库)。理由在它的提交说明里,决策记录本身仍未修订——是否改 ADR 那句请你定。

@vercel

vercel Bot commented Jul 31, 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 Jul 31, 2026 3:45am

Request Review

@os-zhuang os-zhuang closed this Jul 31, 2026
@os-zhuang os-zhuang reopened this Jul 31, 2026
@os-zhuang
os-zhuang force-pushed the claude/d1-d2-strict-default-flip-vbly5z branch from 267b9af to e1efec8 Compare July 31, 2026 03:23
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql.

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

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/plugins/index.mdx (via @objectstack/objectql)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx (via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql)
  • content/docs/releases/v9.mdx (via @objectstack/objectql)

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.

@os-zhuang os-zhuang changed the title fix(migrate): 让值形态门禁自我告知,并停止统计无人执行的字段(#3438 收尾) fix(objectql): 让开门广告停止在它不成立的地方发声(#3438 收尾) Jul 31, 2026
…false (#3438)

The boot line that names an open value-shape gate (#4253) fired on every
deployment there is, and stated something untrue on any deployment that had
already settled the posture with an environment switch. Both are one failure:
an advisory that speaks where it does not apply is exactly how readers learn to
ignore it, and the line's whole value is that a warn-first deployment is told
once, credibly, which command ends warn mode.

Neither defect is reachable from the suite that shipped with the announcement.
`engine.test.ts` mocks `./registry`, and a mocked registry hands the engine
exactly the fields the test wrote — so these cases get a sibling file built on
the genuine `registerApp → registry` path, which is the only way a test sees
what a deployment sees.

`objectHasCoveredValueField` — the dormancy short-circuit meant to spare an
object with no covered field the flag query — tested raw type membership, while
the real registry INJECTS covered-type fields into every object it registers:
`organization_id` and `owner_id` (both `system`), `created_by` and `updated_by`
(both in `SKIP_FIELDS`), four `lookup`s. `validateRecord` skips every one of
them before it reaches the value-shape check, so the short-circuit answered
`true` for literally every object, never fired, and its WeakMap memoized a
constant. It now counts by the validator's own `isScannableValueShapeField` —
the predicate the scanner already imports. Three readings of "a covered field"
drifting by one clause is how a gate ends up governing fields nothing enforces,
which is the shape #4235 argued against.

The announcement also consulted no environment switch, though both postures it
reports on short-circuit ahead of the deployment flag it reads. Under
`OS_DATA_VALUE_SHAPE_STRICT_ENABLED` enforcement is already on for both
classes, so "checked but NOT enforced here" was false; under either opt-out the
operator chose leniency deliberately, so naming a migration that cannot change
what they get is noise. Each gate consults its own pair — `mediaPostureSetByEnv`
and `valueShapePostureSetByEnv`, siblings for the reason `mediaStrictEffective`
and `valueShapeStrictEffective` are siblings, since the opt-outs are per-class
while the opt-in opens both. Cheapest test first, so a kernel with nothing to
say still reaches no flag query.

Enforcement is unchanged: same gates, same flags, same default. The flag read
was already memoized per process, so what this corrects is the property
ADR-0104 states, not a user-visible cost.

The predicate fix is pinned by reverting it: with raw-type membership restored,
`stays silent when no object declares a covered field` fails, because a `note`
declaring only text still carries the four injected lookups.

Verified on a full workspace build: objectql 1345 (7 new), cli 628, spec
check:generated 8/8, check:release-notes and check:doc-authoring pass, ESLint
clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zgA8CQMJeJbFEjnbib1Uv
@os-zhuang
os-zhuang force-pushed the claude/d1-d2-strict-default-flip-vbly5z branch from e1efec8 to 76a72c8 Compare July 31, 2026 03:45
@os-zhuang
os-zhuang marked this pull request as ready for review July 31, 2026 04:04
@os-zhuang
os-zhuang merged commit 956e7f9 into main Jul 31, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/d1-d2-strict-default-flip-vbly5z branch July 31, 2026 04:04
os-zhuang added a commit that referenced this pull request Jul 31, 2026
…ly landed (#3438) (#4299)

The 2026-07-30 addendum's rollout bullet says `os migrate meta --from 16` ends
by naming the two data migrations "with their gate status". The implementation
(#4253) deliberately names them without it, and the reasoning lived only in
that PR's commit message — so the decision record kept prescribing the wrong
half to the next implementer, and already misled one (#4284's first draft
built exactly what the sentence says).

The status half was a category error this ADR had already corrected two
addenda earlier: per the 2026-07-27 addendum the evidence gate is per
DEPLOYMENT, and `meta` acts on SOURCE, which deploys to zero-or-N deployments
— so "its" gate status has no referent at meta time. Best-effort status (show
it when a datastore happens to be reachable) would be worse than none:
silence goes ambiguous, output turns environment-dependent, and the command
gains a hang path on an unreachable database. Status is reported where a
datastore is actually open — each migration command reports before it writes,
and the boot line carries the live verdict, which after #4284 speaks only
where it is true.

Amended in place with the ADR's own convention (the L428-style dated inline
parenthetical), original text preserved. Empty changeset per the
adr-0104-attestation-adr-note precedent. Releases nothing.


Claude-Session: https://claude.ai/code/session_016zgA8CQMJeJbFEjnbib1Uv

Co-authored-by: Claude <noreply@anthropic.com>
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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants