Repository navigation
[Decision] Retire the unserved "Virtual ObjectQL" wire protocol in data-engine.zod.ts (A), or keep it as a planned contract (B)? (#27028) #27274
Description
Filed by domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · 2026-10-28
⛔ Not a claim. ⛔ Not a dispatch. This card asks for one letter. Nothing is built until it is answered.
Filing gate: ② a decision only the maintainer can make. Triage 6459409175 on #27028 says that removing a published wire protocol is a protocol-surface decision, and that the seat brings a decision card with the four facets to the maintainer before removing anything. The measurement is from the #27028 dev thread, phase 1: read-only, with no edit and no PR.
Related: #27028 (tracker; it becomes the execution card on either answer) · #4618 (batch? retirement, ADR-0119 D3) · #26954 · #27182 (the "nobody parses it" retirement route this follows)
维护者速读
- 改了什么:
packages/spec/src/data/data-engine.zod.ts里发布了一整套「Virtual ObjectQL」请求协议(DataEngineRequestSchema及 9 个分支、DataEngineContractSchema、5 个类型,共 16 个导出),设想给另一个进程里的数据引擎用。这样的引擎不存在,也没有任何代码解析它。 - 实测: 本仓 0 个读者(ui/、skills/、examples/、OpenAPI 都是 0);hotcrm、objectui、docs.objectos.ai 也都是 0(对照:它们分别有 311、2095、74 个文件在用 spec)。唯一真正跨 VM 调引擎的地方(QuickJS 沙箱)用的是另一种形状。
- 风险点: 发布出去的 JSON Schema 允许调用方自带
context.isSystem,execute分支是原始 SQL —— 谁要是照着实现,就是一扇提权门。另外content/docs/kernel/contracts/data-engine.mdx还在教已退役的engine.batch?.(),两种选项都会顺手修掉。 - 选项:
| 选项 | 做什么 | 影响 | 工作量 |
|---|---|---|---|
| A 退役(推荐) | 删掉这 16 个导出和 5 个私有辅助;按「无人解析」路线登记(一条 D3 + 10 个 retired-defs),spec minor,不需要 major | 只有直接 import 这些名字的 TS 代码升级时编译报错(实测没有);无存量数据、无运行时行为变化;回滚 = revert | 一个 PR,大部分是删除,远低于 3000 行 |
| B 保留,标为计划中契约 | 描述里加 [EXPERIMENTAL — not enforced],写进 ROADMAP 并指定未来读者 |
假入口继续对外发布;以后真要用,得先收窄(禁止外部传 isSystem、管住 execute)再新建一扇和现有 REST / batch / MCP 重复的数据门(需新 ADR) |
现在很小;兑现时是多包多 PR |
- 推荐 A。 内外都零读者的协议是假入口(AGENTS.md 第 10 条);要兑现它,得先收窄、再造一扇与四扇现有门重复的数据门。A 的同时建议把
DataEngineContractSchema一起退(它已经和IDataEngine对不上,也不生成 JSON Schema),并在协议 18 内以 D3 登记的 minor 落地,而不是等协议 19 的 major。 - 请回:
A / B。
Every reading below was taken on objectstack-ai/objectstack origin/main at
1287b2fa (2026-10-28 13:26Z) unless it names another tree.
The question
packages/spec/src/data/data-engine.zod.ts publishes a serialized request
protocol for "Virtual Data Engines": an engine that lives in another process
and receives its calls over HTTP, a message queue or a plugin boundary. No such
engine exists, and nothing parses the protocol. Do we retire it from the
published surface (A), or keep it and record it as a planned contract with
no reader yet (B)?
What the block is
| Kind | Members |
|---|---|
| Exported request schemas (10) | DataEngineRequestSchema (the method union) and its 9 arms: DataEngineFindRequestSchema, …FindOneRequestSchema, …InsertRequestSchema, …UpdateRequestSchema, …DeleteRequestSchema, …CountRequestSchema, …AggregateRequestSchema, …ExecuteRequestSchema, …VectorFindRequestSchema |
| Exported function contract (1) | DataEngineContractSchema (section 8, z.function() members) |
| Exported types (5) | DataEngineRequest, DataEngineRequestParsed, DataEngineExecuteRequest, DataEngineInsertRequest, DataEngineVectorFindRequest |
| Private helpers used only by the arms (5) | RpcQueryOptionsSchema, foldRpcQueryOptions, RpcLegacyFilterMixin, foldRpcLegacyFilter, RPC_WHERE_SLOT |
These are not part of the block, and A keeps them: RPC_QUERY_ALIAS_SLOTS,
foldQueryAliasSlots and aliasConflictIssue (the query-transport dialect and
ENGINE_WHERE_SLOTS in packages/objectql/src/engine.ts read them), every
*OptionsSchema (IDataEngine is typed with them; DataEngineInsertOptions
alone has 22 referencing files), and QueryTransportParamsSchema /
QueryWithTransportSchema.
Measured
1. Readers in the repo: zero. A whole-tree git grep for the 16 exported
names and RpcQueryOptionsSchema finds 25 files. None parses or imports the
block outside packages/spec/src/data/. ui/ has 0 hits, skills/ has 0,
examples/ has 0, and the generated OpenAPI document (json-schema/openapi.json
in the 18.2.0 tarball) has 0. The hits are:
- the defining file and its own test (
data-engine.test.ts: 10describe
suites, part of the integration suite, and 5 rows of the projection-parity
table); - generated ledgers:
api-surface/data.json16,export-origins/data.json16,
declaration-map/data.json14,json-schema.manifest/data.json10,
authorable-surface/data.json35 rows over the 9 arms,
authorable-defaults/data.json1,dropped-refinements.baseline.json
8 entries,docs-import-surface.baseline.json6,
unemitted-schemas.baseline.json1 (DataEngineContractSchema),
variant-docs.json1 (method:aggregate|…|vectorFind), and
type-alias-convention.pin.test.ts3Iso_rows; - the generated reference page
content/docs/references/data/data-engine.mdx
(24 mentions) andcontent/docs/references/index.mdx(1); - prose only:
contracts/data-engine.ts:61("the RPC (Virtual Data Engine)
boundary"),ui/view-grouping-query.ts:172,objectql/src/engine.ts:601,
DEVELOPMENT_PLAN.md(2 rows), ADR-0119 line 61 (history, no edit needed),
the chore(spec): retire IDataEngine.batch? per ADR-0119 D3 — declared-but-unimplemented, zero callers #4618 D3 entry17.data-engine-batch-retired.ts, and CHANGELOGs.
The nearest real boundary chose a different shape. The QuickJS hook and action
sandbox (runtime/src/sandbox/quickjs-runner.ts:696-703) is the one place
where engine calls cross a VM boundary today. It exposes
ctx.api.object(name).find/findOne/count/aggregate/insert/update/delete/updateMany/deleteMany/upsert,
gated by capabilities, and parses no schema at the crossing. The wire batch
validates with CrossObjectBatchRequestSchema / BatchUpdateRequestSchema from
api/batch.zod.ts.
DataEngineContractSchema has already drifted from the real contract.
IDataEngine reads take a trailing options?: BaseEngineOptions
(contracts/data-engine.ts:320-323), and it declares vectorFind? and
execute?. The Zod contract has neither.
2. What is published. @objectstack/spec@18.2.0 (npm latest, protocol
18.0.0) ships the block in four places:
json-schema/data/DataEngine{,Find,FindOne,Insert,Update,Delete,Count,Aggregate,Execute,VectorFind}Request.json
(10 files) plus the bundle json-schema/objectstack.json; the dist/*.d.ts;
src/data/data-engine.zod.ts; and api-surface/. Each JSON Schema carries
$id: https://schema.objectstack.io/v18.2.0/data/DataEngineRequest.json, and so
on. Nothing in this repo deploys that host. No workflow names it, and its only
appearance is SCHEMA_BASE_URL in build-schemas.ts. Whether the host serves
anything is NOT MEASURED: this container's proxy refuses the CONNECT. The
exports map has no ./json-schema/* subpath, so the JSON files are reachable
only by a file path into node_modules. The reference page is published on the
docs site.
3. Outside readers: none found. Shallow clones, read-only, searched for the
16 names, RpcQueryOptions, "Virtual ObjectQL" and "Virtual Data Engine",
CHANGELOGs excluded. Each repo has a control: the number of files that do import
@objectstack/spec.
| Repo @ commit | Block hits | Control: files importing @objectstack/spec |
|---|---|---|
hotcrm @ 353dace (2026-10-28) |
0 | 311 |
objectui @ b2c0ade (archived, 2026-10-26) |
0 | 2095 |
docs.objectos.ai @ cab35fd (2026-10-28) |
0 | 74 |
NOT MEASURED: third-party npm dependents and public GitHub code outside the
org. This session's code search is scoped to this repo.
4. Docs that teach it. The hand-written page
content/docs/kernel/contracts/data-engine.mdx imports DataEngineRequest for
one purpose: a batch?(requests: DataEngineRequest[], …) member in its
IDataEngine listing (line 52), with a worked
engine.batch?.([{ method: 'insert', … }]) example (lines 572-582). batch?
was retired by ADR-0119 D3 (#4618). An author who copies the example gets a tsc
error on a typed engine. On an untyped one, ?. makes the three inserts a
silent no-op. This page is wrong under either option and is fixed in the same PR
whichever way the maintainer decides.
5. The published JSON Schema is not a safe wire contract as written.
BaseEngineOptionsSchema.context is ExecutionContextSchema. So every read and
write arm admits a caller-supplied context carrying isSystem, userId,
permissions and positions. "isSystem" is present in the published
DataEngineFindRequest.json. The execute arm carries a raw driver command
(command: unknown, the SQL escape hatch). Eight of the ten request defs are
listed in dropped-refinements.baseline.json. That means a validator using the
published JSON Schema accepts requests that the Zod parse refuses, such as the
alias-conflict folds.
Option A: retire (recommended)
What changes. One PR, packages/spec plus docs:
- Delete the 10 request schemas,
DataEngineContractSchema, the 5 types and the
5 private helpers. Leave a short in-file note where the block stood, in the
same style as theDataEngineBatchRequestSchemanote. - Registration follows the "nobody parses it" route, the precedent being
18.document-schemas-retired(spec(field): retire the@deprecatedCurrencyValueSchema(still exported atfield.zod.ts:581) — ADR-0104 D1 says deleted; prerequisite of the multi-currency epic #8345 #27182):- one D3 semantic entry,
entries/semantic/18.data-engine-rpc-protocol-retired.ts; - 10
entries/retired-defs/18.data__DataEngine*Request.tsfiles for the
defs leavingjson-schema.manifest/; - no
retiredKey()tombstone and no D2 conversion, because no metadata
source authors these and no parse exists for a prescription to reach; - changeset:
@objectstack/specminor,Clause-②: yes (narrowing),
Metadata-protocol: narrowed(theauthorable-surface/rows move, which
rules outnone), marker<!-- adr-0087: registered data-engine-rpc-protocol-retired -->,
and a FROM → TO table. FROMDataEngine*RequestSchemaTO the doors that
exist:IDataEnginein process, REST/data/:object, andPOST {basePath}/batch.
- one D3 semantic entry,
- The
runtime-export-retired(D9) andtype-surface-only(D8) markers do not
apply. D8 predicate 2 refuses any diff underpackages/spec/**. - No major is needed, and none is allowed.
check-changeset-no-major
refusesmajorin the launch window, and ADR-0087 lets a pre-GA break ship
one-step as long as it lands as a chain step (the D3 entry above). - Generated and baseline files that move (regenerated, never hand-edited):
api-surface/data.json−16,export-origins/data.json−16,
declaration-map/data.json−14,json-schema.manifest/data.json−10,
authorable-surface/data.json−35 (leaveauthorable-surface.base.json
alone),authorable-defaults/data.json−1,dropped-refinements.baseline.json
−8 entries plus itsmeasuredtotals,docs-import-surface.baseline.json−6,
unemitted-schemas.baseline.json−1,variant-docs.json−1,
type-alias-convention.pin.test.ts−3 rows (the census count moves too), and
the reference page plus its index row. - Hand edits: the 10 test suites and the projection-parity rows; the contracts
docs page (import,batchmember,batchexample); the three comment
sites, one of which ispackages/objectql/src/engine.ts, so that package
needs a patch changeset or the comment rides an unpublished path; and the
2DEVELOPMENT_PLAN.mdrows. - Packages that move:
@objectstack/spec(minor) and, for the comment only,
@objectstack/objectql.ui/does not move, so no Type Check UI fix is
needed. - Governed surfaces: none expected.
skills/*/references/_index.mdnames the
file by its header line, and the file stays. That is NOT MEASURED until
phase 2 runscheck:generated --fix; if it moves an_index.md, the PR
becomes Tier H. - Size: estimated well under the 3,000-line threshold. Most of it is deletions.
Why. It is a declared capability that nothing delivers, the ADR-0049
"remove" arm, and AGENTS.md Prime Directive #10 says not to advertise one.
Measured readers are zero in this repo and in the three sister repos. The only
real cross-boundary engine surface uses a different shape. The published JSON
Schema would be an escalation door if anyone implemented it as written
(context.isSystem, raw execute). The block costs every engine-options card
a tax: #4618 and #26954 both had to edit it without anyone using it.
Risk. An unknown TypeScript consumer importing these names gets a compile
error on upgrade. The compiler is the channel, and the D3 entry and changeset
name the replacements. A tool that reads
node_modules/@objectstack/spec/json-schema/data/DataEngine*Request.json finds
the files gone in the new version. Versioned $id URLs for old versions are
unaffected. No stored metadata and no runtime behaviour changes.
Rollback. Revert the PR. It is pure deletion with no data at rest and no
conversion to undo. The revert removes the retired-defs entries together with
the re-added schemas, so the RETIRED_DEFS_BY_MAJOR "still published" gate
stays consistent.
Option B: keep, and record it as a planned contract
What changes.
- Add
[EXPERIMENTAL — not enforced]to the union's and the 9 arms'
.describe()(ADR-0049'sexperimentalarm). This movesgen:schema/
gen:docsand the reference page. - Rewrite the
#4618note so it names spec: the Virtual Data Engine wire protocol block (DataEngineRequestSchemaand its arms) has zero readers — decide ADR-0049 enforce-or-remove; its note says "tracked separately" but nothing tracks it #27028 and the planned reader. - Add a ROADMAP entry naming the reader and its owner.
- Fix the contracts docs page's
batchsection, which is needed either way.
What enforcing it later costs (the reader it would need).
- A door. A route or queue consumer, owned by exactly one package, that
parsesDataEngineRequestSchemaand dispatches toIDataEngine. - Narrowing first. The door has to bind the caller's identity into
ExecutionContextand refuse a wire-suppliedcontext(above all
isSystem). It has to refuse or capability-gateexecuteremotely, and drop
context.transaction(anunknownhandle that cannot cross a wire). That is
a narrowing of the published protocol before its first reader. - A client. A
RemoteDataEngine implements IDataEngineto produce the
requests. - Discovery. Advertise the door, and mount everything advertised
(ADR-0076 D12, AGENTS.md "Machine-readable surfaces must not lie"). - Overlap. It duplicates the existing doors: REST
/data/:object,
POST /batch, the metadata protocol'sfindData/batchData, MCP tools
and the client SDK. That is why AGENTS.md route-ownership rule 1 would need
an ADR to justify a second door, and a new ADR is Tier H. - Size. A multi-package, multi-PR effort, with no North Star row it
advances today (docs/NORTH-STAR.mdandROADMAP.mdhave 0 hits for the
data engine RPC).
Risk. The false affordance stays published. AI authors and codegen keep
reading a protocol that nothing serves, and the JSON Schema keeps advertising a
caller-supplied isSystem. The tax on every engine-options change continues.
Rollback. Trivial. Remove the markers.
What we ask the maintainer
- A or B? The recommendation is A. A protocol with zero readers,
inside or outside the repo, is a false affordance. Enforcing it would first
require narrowing it and building a second data door that duplicates four
existing ones. - If A: retire the whole set,
DataEngineContractSchemaincluded (it has
drifted fromIDataEngineand is unemitted), and land it as a D3-registered
minor inside protocol 18 rather than waiting for a protocol 19 major. Both
are the recommendation.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 28, 2026
Brief-claimed-by: session_01QSeKYo7qYsX6HsAneHBMP2 · batch #424 · 2026-10-28T14:26Z
Director seat, summon #53 (ledger objectstack#12708). Presented to the maintainer in batch #424 as item 1 of 1 — the spec seat's decision card on the unserved "Virtual ObjectQL" wire protocol (#27028's phase-1 measurement).
Seat increment (re-read at
origin/main0d45c0e7, 2026-10-28T14:26Z):packages/spec/src/data/(generated ledgers, CHANGELOGs and JSON excluded) → 6 files, all prose or pins (content/docs/references/data/data-engine.mdx,references/index.mdx,DEVELOPMENT_PLAN.md,type-alias-convention.pin.test.ts,ui/view-grouping-query.tsprose, one changeset) — no parser, no importer (control:DataEngineInsertOptions, kept by A, has 22 reader files).content/docs/kernel/contracts/data-engine.mdx:52, 577still teaches the retiredbatch?(ADR-0119 D3).IDataEnginedeclaresvectorFind?(contracts/data-engine.ts:328) andexecute?(:343) that the Zod contract lacks — the drift is real.data-engine.zod.ts:76context: ExecutionContextSchema.partial().optional()admits a caller-suppliedisSystem— the published JSON Schema is an escalation door if anyone ever implements it. The one real cross-VM boundary (runtime/src/sandbox/quickjs-runner.ts:17) usesctx.api.object(name).…, a different shape.IDataDriverbehind an ordinary object). Retiring the former touches nothing the latter uses; it also removes a would-be second door for remote engines, which [Decision] Ontology over external systems: virtual objects on ADR-0015 federation — two driver tiers (plugin code · declarative OpenAPI), pushdown/identity/catalog/cache rules, Studio and AI drafts (2026 ontology research + deep design) #27261 lists among its non-goals.isSystem与execute再造一扇与 REST / batch / MCP / 沙箱重复的门——那是新 ADR 的事,不是保留一个草稿。② 拉动:仓内外零读者(hotcrm 0 / objectui 0 / docs 0,对照 311 / 2095 / 74)。③ 防错:发布的 JSON Schema 接受调用方自带isSystem与原始 SQL;A 关掉它,B 留着它。④ 不扩散:A 是删除 + D3 登记,minor;B 是 declare-and-maintain。@deprecatedCurrencyValueSchema(still exported atfield.zod.ts:581) — ADR-0104 D1 says deleted; prerequisite of the multi-currency epic #8345 #27182's route and ADR-0119 D3 / chore(spec): retire IDataEngine.batch? per ADR-0119 D3 — declared-but-unimplemented, zero callers #4618);DataEngineContractSchemagoes with them (already drifted, no JSON Schema emitted); the kernel docs page dropsbatch?and its example in the same PR. Not B: a planned contract with no reader and an unsafe published shape.Clause-②: no (narrowing)·Metadata-protocol: narrowed(an export surface removed; no authorable key — the spec seat confirms the arm);Skills-docs: noneexpected.Prior rulings read: Virtual ObjectQL · DataEngineRequest · batch? → ADR-0119 D3 (#4618), #26954, #27182 (route); thread: none (0 comments); #27028 triage 6459409175.
objectstack-fleet commentedon Oct 28, 2026
Ruling recorded ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-28T14:40ZThe maintainer chose A (retire) at 2026-10-28T14:38Z, on the decision card the seat posted in the project thread alongside this issue. It answers the question this card asks, and only that question.
What it settles:
DataEngineRequestSchemaand its 9 arms,DataEngineContractSchema, the 5 types and the 5 private helpers.@objectstack/specminor inside protocol 18, following the "nobody parses it" route: one D3 semantic entry plus 10retired-defsentries. There is no tombstone and no conversion.content/docs/kernel/contracts/data-engine.mdxbatch?section is fixed in the same PR.Execution card: #27028. This card's question is answered, and the seat closes it once the claim is posted there.
objectstack-fleet commentedon Oct 29, 2026
Decided and landed ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-29T03:03ZThe maintainer chose A, retire (ruling record
6472013580). It landed as PR #27324 (merge9ac5ae5b) under #27028. This decision card is closed as completed.