diff --git a/.changeset/27346-data-engine-rpc-retirement-wording.md b/.changeset/27346-data-engine-rpc-retirement-wording.md new file mode 100644 index 00000000000..b778067ef93 --- /dev/null +++ b/.changeset/27346-data-engine-rpc-retirement-wording.md @@ -0,0 +1,10 @@ +--- +'@objectstack/spec': patch +--- + +docs(spec): the Virtual ObjectQL retirement's D3 reason says which request arms admitted a caller-supplied `context` + +Clause-②: no +Metadata-protocol: none + +Supersedes, in the same release, one sentence of the `feat(spec)!: retire the unserved Virtual ObjectQL request protocol from data/data-engine.zod.ts` note: "every arm admitted a caller-supplied `context` (`isSystem` included)". The corrected wording is: every read and write arm (`find`, `findOne`, `insert`, `update`, `delete`, `count`, `aggregate`) admitted a caller-supplied `context` (`isSystem` included). The `execute` arm carried an untyped options record rather than a `context` key, and the `vectorFind` arm had no `context` at all. The D3 semantic entry `data-engine-rpc-protocol-retired` carries the same correction, so `os migrate meta` and the upgrade guide read it. The `strictReadonlyWrites` docblock on `WriteObservabilityOptions` no longer names the retired "Virtual Data Engine" boundary: the option cannot be serialized and is not reachable from a REST/wire body. No schema, export or runtime behaviour changes. diff --git a/content/docs/protocol/objectql/security.mdx b/content/docs/protocol/objectql/security.mdx index 0f841f792a0..6ea15e42a00 100644 --- a/content/docs/protocol/objectql/security.mdx +++ b/content/docs/protocol/objectql/security.mdx @@ -390,8 +390,8 @@ which the engine computes on every read and no driver has a column for, is dropp every write in every context, `isSystem` included. The vocabulary is open — it grows as the write path gains legal strips — so branch on `reason` exhaustively rather than treating "not `readonly_when`" as "read-only". The listener is an in-process -callback: it is delivered by the local engine, and does **not** cross the RPC / Virtual -Data Engine boundary, so a remote caller never receives these events. Without a listener, +callback: it is delivered by the local engine, it cannot be serialized, and no REST/wire +body reaches it, so a remote caller never receives these events. Without a listener, the only trace is a server-side `WARN` naming the object, the field, and both remedies. --- diff --git a/docs/audits/2026-07-unknown-key-strictness-ledger.md b/docs/audits/2026-07-unknown-key-strictness-ledger.md index 4784ee03e55..b2f6c8c13cc 100644 --- a/docs/audits/2026-07-unknown-key-strictness-ledger.md +++ b/docs/audits/2026-07-unknown-key-strictness-ledger.md @@ -715,7 +715,7 @@ column does not move and the `strip` column falls by the count of what left. | File | Class | Note | |---|---|---| | `object.zod.ts` | authorable | top-level already guarded (#1535); **all 14 inner blocks strict** — 13 at 批 20, and the held 14th (`IndexSchema`) closed 2026-08-16 once its hold's evidence was spent: the hold was the #5114 class caught pre-ship (objectui's `FALLBACK_SCHEMAS.index` had drifted, offering `where`/`brin`, and the editor PUT the whole object through `saveMetaItem`), gated on the objectui rename (#5247) and an ADR-0049 answer for `type`/`partial` (#5248). #5248 answered remove (PR #5842, `retiredKey` tombstones + protocol-17 migration); **objectui#4772 then converged the editor to the declared `name`/`fields`/`unique` surface**, so the close shipped with a curated `where` guidance entry (database-layer prescription — deliberately NOT a rename onto the retired `partial` tombstone, which would be finding 7) and per-key pins that the tombstones' own prescriptions survive the strict path. Semantic entry `object-index-unknown-keys-refused` (protocol 18). The file's row left the remaining-strip map by reaching zero; the hold's full history is preserved in that map's section prose and in `object-strictness-batch20.test.ts` §4, which flipped from pinning the strip to pinning the close | -| `data-engine.zod.ts` | wire (p) | engine contract shapes (was 14 — `DataEngineBatchRequestSchema` retired with `IDataEngine.batch?`, #4618) | +| `data-engine.zod.ts` | wire (p) | engine contract shapes (was 14 — `DataEngineBatchRequestSchema` retired with `IDataEngine.batch?`, #4618; ten more sites — `DataEngineRequestSchema` and its nine arms — retired with the Virtual ObjectQL request protocol, #27028) | | `seed-loader.zod.ts` | wire | **re-verdicted 2026-08-14 (#4001 batch D)** — the `(p)` split's "authored" half dissolved under measurement. The seed FILE shape this file's old note meant is `SeedSchema` (`seed.zod.ts`, a registered type, strict since the registered-types batch) — it enters this file only as the `seeds[]` VALUE inside `SeedLoaderRequestSchema`, so the authored half was never one of these 12 sites. Every producer of the request/config halves is framework code, enumerated: `runtime/app-plugin.ts` ×3 (org replay / inline seed / hot-reload), `runtime/domains/packages.ts`, `metadata-protocol/protocol.ts` `applySeedBodies`, `metadata-protocol/seed-loader.ts` `validate()`, `cloud-connection` marketplace install — all `.parse()`/`.safeParse()` over config LITERALS written in code; no `defineStack` key, no metadata type, no Studio form, no CLI flag writes a loader config. `SeedIdentity` is server-constructed by design (`execution-context.zod.ts`: "never client-supplied"); graph/resolution/result/error shapes are built by the loader itself (`buildDependencyGraph`, `buildResult`). BFS from all 26 metadata-type roots + `ObjectStackSchema` (6689 nodes; `ObjectSchema` positive control REACHABLE, fresh uncarried shape negative control UNREACHABLE, synthetic-carrier flip green, same run): all 12 UNREACHABLE. Same class as `data-engine.zod.ts` — an internal service contract (`ISeedLoaderService`), machine-fed at every door | | `field.zod.ts` | authorable | partially strict | | `filter.zod.ts` | open | query dialect — user data flows through the predicate values; validated semantically elsewhere | diff --git a/packages/spec/src/contracts/data-engine.ts b/packages/spec/src/contracts/data-engine.ts index 8c3c210504e..d725f7dc971 100644 --- a/packages/spec/src/contracts/data-engine.ts +++ b/packages/spec/src/contracts/data-engine.ts @@ -169,8 +169,8 @@ export interface WriteObservabilityOptions { * * This is an IN-PROCESS option, exactly like its neighbour above, and for the * same structural reason: `WriteObservabilityOptions` is not the serializable - * `EngineUpdateOptionsSchema` bag, so nothing here crosses the RPC / Virtual - * Data Engine boundary or is reachable from a REST/wire body. A remote caller + * `EngineUpdateOptionsSchema` bag, so nothing here can be serialized, and + * none of it is reachable from a REST/wire body. A remote caller * cannot set it, and gets NEITHER behaviour: its write is stripped and * committed, silently from its side, exactly as before this option existed — * a 200 whose read-only columns kept their stored values. That is deliberate diff --git a/packages/spec/src/data/data-engine.zod.ts b/packages/spec/src/data/data-engine.zod.ts index cd3547b66cf..f468673a2ef 100644 --- a/packages/spec/src/data/data-engine.zod.ts +++ b/packages/spec/src/data/data-engine.zod.ts @@ -1343,8 +1343,8 @@ export type QueryWithTransportParsed = z.infer; // `ctx.api.object(name).find(...)` and parses no request schema; the wire batch // validates with `CrossObjectBatchRequestSchema` / `BatchUpdateRequestSchema` // from `../api/batch.zod.ts`. As published the protocol was also unsafe to -// implement: every arm admitted a caller-supplied `context` (`isSystem` -// included), and `execute` carried a raw driver command. Its private helpers +// implement: every read and write arm admitted a caller-supplied `context` +// (`isSystem` included), and `execute` carried a raw driver command. Its private helpers // (`RpcQueryOptionsSchema` and the `filter`-only fold) went with it; // `RPC_QUERY_ALIAS_SLOTS` above stays, since the transport dialect and the // engine fold by it. diff --git a/packages/spec/src/migrations/entries/semantic/18.data-engine-rpc-protocol-retired.ts b/packages/spec/src/migrations/entries/semantic/18.data-engine-rpc-protocol-retired.ts index 8e565ff8511..fff540fba4c 100644 --- a/packages/spec/src/migrations/entries/semantic/18.data-engine-rpc-protocol-retired.ts +++ b/packages/spec/src/migrations/entries/semantic/18.data-engine-rpc-protocol-retired.ts @@ -36,7 +36,8 @@ export const entry: SemanticMigration = { + 'against controls of 311, 2095 and 74 files importing `@objectstack/spec`. The one real ' + 'boundary engine calls cross, the QuickJS hook and action sandbox, speaks ' + '`ctx.api.object(name)` and parses no request schema. As published the protocol was also ' - + 'unsafe to implement: every arm admitted a caller-supplied `context` (`isSystem` included) ' + + 'unsafe to implement: every read and write arm admitted a caller-supplied `context` ' + + '(`isSystem` included) ' + 'and the `execute` arm carried a raw driver command, so enforcing it would first have ' + 'meant narrowing it and building a second data door beside REST, batch, MCP and the ' + 'sandbox. `DataEngineContractSchema` restated the engine methods as `z.function()` '