feat(spec)!: retire datasource.capabilities — 十一个无人读的标志,其中一个是安全声明(#4583 批 A) - #4601
Merged
Merged
Conversation
…ad, one a safety claim (#4583) DatasourceCapabilities declared eleven booleans — transactions, seven query* flags, joins, fullTextSearch, readOnly, dynamicSchema — all strict-guarded, all read by nothing. Pushdown is decided by the runtime driver's own `supports.*`, a different mechanism, so declaring `queryAggregations: false` never once changed which engine path ran. Removed rather than bridged: there was nothing on the other side to connect it to. readOnly is why this is not tidy-up. It reads as a safety property and was authored as one — the shipped CRM example labelled a datasource "CRM Analytics Read Replica" on its strength while the datasource took writes like the primary. The key had already been MOVED twice toward somewhere it might be enforced (out of `config` in #4410, into `capabilities` in #4465) and was inert at every address. This removes it instead of moving it a third time. Removing it does NOT hand the author a replacement, and the rejection says so. `external.allowWrites: false` is the one enforced write gate and applies only to FEDERATED datasources — assertWriteAllowed returns early for a managed (or unset-schemaMode) one, so that key would be equally inert for a local database. A managed datasource has no read-only gate at all; that gap is #4584, deliberately not invented here. Also fixed: READ_ONLY_BELONGS_ON_DATASOURCE — the prescription every SQL driver shares for a `readOnly` written inside `config` — was still sending authors TO the removed key. A prescription that lands on an inert key manufactures exactly the belief it was meant to correct. DriverDefinition.capabilities goes with it: its call graph is closed too — MemoryDriverSpec / MongoDriverSpec are referenced only by their own tests, so the whole schema object is inert, not just the datasource-side use. Route is strict-removal (both shapes are .strict()): keys leave the walked shape, so the 11 ledger rows are DELETED rather than flipped, and the 13 baseline lines in authorable-surface.json are removed deliberately in this PR — the readReplicas (#4468) precedent. ADR-0087 conversion `datasource-capabilities-removed` is registered in step 17, so `os migrate meta --from 16` really does rewrite author sources rather than the tombstone merely claiming it. datasource ledger: 20 dead -> 9 (healthCheck x3, retryPolicy x4, external x2 — batches B/C/D of #4583). Strictness-ledger site count 9 -> 8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5CYr5SDwe85gH2Jr5KSgu
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 2, 2026 09:47
os-zhuang
enabled auto-merge
August 2, 2026 09:48
Conflict was in `content/docs/references/data/datasource.mdx` — a GENERATED file, conflicting because both sides regenerated it: main's #4595 taught the generator to spell import examples from the real export surface, while this branch removed `DatasourceCapabilities` from that surface. Resolved by regenerating rather than hand-merging. The result is exactly what the two changes imply together — #4595's corrected spelling minus the removed export: import { DatasourceSchema, DriverDefinitionSchema, DriverType, ExternalDatasourceSettingsSchema } from '@objectstack/spec/data'; #4595 also added an import-surface baseline, which listed `data/DatasourceCapabilities — no type export` as a known gap. This removal closes that gap, and the baseline is shrink-only, so the stale line is deleted (--update-import-baseline) — a stale exemption would otherwise stay available to excuse the NEXT missing export. The three auto-merged baseline JSONs (authorable-surface, api-surface, json-schema.manifest) were not trusted as merged text: `gen:schema` rewrites them wholesale once its vanished-key gate passes, and the full build confirms no residue of either side's removals. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5CYr5SDwe85gH2Jr5KSgu
This was referenced Aug 2, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4583 的批 A。B/C/D(
retryPolicy/healthCheck/external两键)留在那条 issue 下可并行认领。问题
DatasourceCapabilities声明了十一个布尔位 ——transactions、七个query*、joins、fullTextSearch、readOnly、dynamicSchema—— 全部 strict 校验,全部无人读取。下推判断读的是运行时 driver 自己的supports.*(objectql/src/engine.ts:3671, :4529, :4810),是另一套机制:一个声明queryAggregations: false的 datasource 从未因此改变过引擎走哪条路径。所以这里是 remove 而非 enforce —— 对面没有可连接的东西。这也是 #4487 判定时给这十一条写的
authorHint无一例外都是 "Delete it" 的原因。readOnly:这不是顺手清理它读起来像安全属性,也确实被当作安全属性来写。
examples/app-crm里一个标着 "CRM Analytics Read Replica" 的 datasource 就靠它声称只读,而那个 datasource 和主库一样照常接受写入。更值得记一笔的是它的履历:这个键已经被搬过两次,每次都是朝着"也许那边会强制执行"的方向 —— #4410 把它移出
config,#4465 把它移进capabilities—— 每一个地址上都是惰性的。本 PR 删掉它,而不是搬第三次。而且直到这次改动之前,
packages/spec/src/data/driver/common.zod.ts的READ_ONLY_BELONGS_ON_DATASOURCE(所有 SQL driver 共享的处方)仍在把作者指向这个键。一条落在惰性键上的处方,恰好制造出它本要纠正的那个误解。删掉它并没有给作者替代方案,报错必须说清这一点
账本原本给的替代是
external.allowWrites: false。读一下assertWriteAllowed:它只管联邦库。 对一个本地的、managed 的 datasource(crm 那个正是),这个键同样是惰性的 —— 把作者从一个惰性键指向另一个惰性键,就是把本次要退役的缺陷洗一遍再发出去。
managed datasource 目前根本没有只读闸门。这个缺口是 #4584,本 PR 刻意不就地发明机制(照 #4479 的先例)。墓碑因此明说了它不适用于哪种情况,并建议在数据库侧用 SELECT-only 账号 —— 那是真边界。
顺带闭合的一条调用图
DriverDefinition.capabilities一并删除,不是"顺着 datasource 侧判死":MemoryDriverSpec/MongoDriverSpec只被它们自己的测试引用,无任何运行时消费者,所以整个 schema 对象都是惰性的。路线与产物
strict-removal(两个形状都是
.strict()):键离开 walked shape ⇒ 11 条账本行删除而非改状态(留着会被orphans.mts判 ORPHAN)。datasource-capabilities-removed注册进 step 17,所以墓碑里那句os migrate meta --from 16是真能跑的,不是空话(fixtureexpectedNotices: 1)authorable-surface.json删 13 行基线(8318 → 8305)—— 故意为之,readReplicas(datasource.readReplicas 声明了、strict 了、刚被加了校验——但没有任何消费者(ADR-0049 enforce-or-remove,#4410 收尾发现) #4468)的先例就是同 PR 删行;这个机制本身可被无声重写的弱点是 authorable-surface.json 只能发现、不能阻止 spec 键被移除——gen:schema会无声重写基线(#4525 钉子暴露的系统性弱点) #4563json-schema.manifest.json去掉DatasourceCapabilities(构建闸门明确要求,否则下次gen:docs会静默删掉它的参考页)datasource.zod.ts站点数 9 → 8,data/段总计 165 → 164验证
datasource.test.ts):写capabilities被拒;写顶层readOnly的报错同时包含external.allowWrites和managed—— 后者是防止处方退化成"改个键名"的钉子;driver definition 上的capabilities同样被拒pnpm build/pnpm test(132 tasks 全绿)/pnpm typecheck/pnpm lint全部通过check:liveness、check:empty-state、check:variant-docs、check:strictness-ledger、check:generated、check:i18n,外加check-slot-lookup-ratchetgen:spec-changes/gen:upgrade-guide/gen:api-surface/gen:docsquick-reference.mdx的类型表已去掉DatasourceCapabilities;⛔content/docs/releases/未触碰datasource账本:dead 20 → 9(剩healthCheck×3、retryPolicy×4、external×2 = 批 B/C/D)。给 reviewer 的两点
readOnly的处理是否同意:删除 + 指向 managed datasource 没有任何只读闸门:external.allowWrites 只管联邦库,本地库声明只读无处可写(#4583 移除 capabilities.readOnly 后的正式立项位) #4584,而不是就地加一个只拦 ObjectQL 写路径(拦不住直连 / 迁移 / DDL)的布尔位。我在 managed datasource 没有任何只读闸门:external.allowWrites 只管联邦库,本地库声明只读无处可写(#4583 移除 capabilities.readOnly 后的正式立项位) #4584 里倾向"不建,靠数据库账号权限",但那是那条 issue 的决策。authorable-surface.json那 13 行是我按readReplicas先例主动删的。若你认为该走别的确认方式,这一处最容易改。Generated by Claude Code