docs(kernel): align data-engine contract page with the shipped engine seam (#7057) - #7173
Merged
Merged
Conversation
… seam (#7057) Three measured divergences on content/docs/kernel/contracts/data-engine.mdx, all verified against origin/main rather than the issue's line anchors. 1. `EngineQueryOptions.cursor` and 2. query-level `distinct` were retired by #4286 (ADR-0049 / ADR-0078) but were still listed as live members of the interface. They are not merely absent from the schema: `retiredKey()` tombstones REJECT them by name, so a reader copying this block wrote a query the engine refuses. Both lines are removed from the code block and replaced by a "Removed in protocol 17" subsection carrying each tombstone's own migration prescription (keyset as a `where` predicate on the sort key; `distinct(object, field)` / `groupBy` / `count_distinct`). 3. The `WriteObservabilityOptions` section stopped at #3407 and never learned #5126's `strictReadonlyWrites` — absent from both prose and the code block — so "The write still succeeds" read unconditionally where strict refuses the write with ERR_READONLY_FIELD_REJECTED. The strip enumeration also still named only the two author-declared strips, missing the runtime-owned strip (#5503, the one that also runs on INSERT) and the primary-key strip (#6437), even though #7125 had already repaired the `reason` enum in the code block. The section is rewritten against packages/spec/src/contracts/data-engine.ts: a strip table (strip / reason / verbs / writers it skips), the two options as alternative outputs of one seam (`onFieldsDropped` does NOT fire on a refused write), the INSERT rule and its two exempt writers, and the engine-seam vs DataProtocol-ingress layering note (#3043; `preserveAudit` is UPDATE-only at the ingress, #6640) — that layering verified in metadata-protocol's `stripReadonlyForInsert`, not taken on trust. The in-process-only Callout now covers the whole bag, since a client toggling write-refusal is the specific thing #5126 ruled out. Docs-only; no package behaviour changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KJATVrh6V2ysutYUJigh3B
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-help
marked this pull request as ready for review
August 10, 2026 02:04
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.
Fixes #7057
Docs-only. Three measured divergences on
content/docs/kernel/contracts/data-engine.mdx, each re-derived againstorigin/main(@5087ac6) rather than against the issue's line anchors — the file moved twice on 2026-08-09 (#7125, then the tree advanced again during this task).1 + 2.
EngineQueryOptions.cursorand query-leveldistinct— retired by #4286Both were still listed as live members of the interface. They are not merely gone from the schema:
packages/spec/src/data/data-engine.zod.tskeeps aretiredKey()tombstone for each, so a query carrying one is rejected by name. The page was telling readers to write a key the engine refuses — copy-the-docs-and-fail, exactly the class the triage ruling named.Both lines are removed from the code block and replaced by a
Removed in protocol 17subsection carrying each tombstone's own migration prescription:cursor— express the keyset as an ordinarywherepredicate on your sort key with the matchingorderBy;QueryBuilder.cursor()went with it.distinct—distinct(object, field)on the SQL/memory drivers,groupByfor unique combinations, thecount_distinctaggregation for a deduplicated count.The prose follows this file's own house style for a removal (the
AggregationNode.distinctnote added by PR #7051 sits ~200 lines below).3.
WriteObservabilityOptions— stopped at #3407The PM's dispatch table called this row "partly overtaken by #7125". Measured, that is right for the code block and wrong for the prose, so the repair is larger than the table implies:
origin/mainreasonenum missingprimary_keystrictReadonlyWritesabsent from the code blockRewritten against
packages/spec/src/contracts/data-engine.ts(read-only for this card):reason/ verbs / writers it skips — covering all four legal strips and the fact that the runtime-owned one reports under thereadonlyarm rather than adding a new one;strictReadonlyWritesin the code block and a section of its own: refuses before the driver is touched, nothing written, coverage DERIVED from whatonFieldsDroppedreports,ReadonlyFieldRejectedError/ERR_READONLY_FIELD_REJECTEDcarrying the full field list, caught bycodenotinstanceof;onFieldsDroppeddoes not fire on a write strict refuses;isSystemand thepreserveAudithistorical import (data import: a "historical" import can't preserve original timestamps / audit fields — updated_at is stamped now, readonly fields stripped on upsert (#3479 follow-up) #3493);onFieldsDroppedto the whole bag, because a client toggling write-refusal on a security-adjacent path is the specific thing the [决策] readonly 剥离的 strict/reject 模式落在哪一层 —— B(WriteObservabilityOptions)推荐,A/C/D 各有代价(#4903 后续) #5126 ruling excluded.The layering claim was verified, not relayed. The issue thread suggested carrying over the engine-seam vs DataProtocol-ingress distinction (#3043 / #6640).
packages/metadata-protocol/src/protocol.tsstripReadonlyForInsertconfirms it in code: the ingress enforces its own author-declaredreadonlypolicy on create, knows onlyisSystem, deliberately does not readpreserveAudit(#6640), and leaves runtime-owned types to the engine strip (#5503). That is what the new Callout says, and it says nothing wider.Verification
Premise confirmed at the schema level, then reverse-verified by parsing (a probe run against
EngineQueryOptionsSchema, not committed):Gates run locally (docs-facing families for this surface; the full farm is CI's):
pnpm check:doc-authoring— green, 374 files cleanpnpm check:docs-audit-scope— green, 179 hand-written docs in syncpnpm --filter @objectstack/lint run check:doc-formula-expressions— green, 22 examples across 387 filespnpm --filter @objectstack/spec run check:skill-examples— green, 209 prose examples type-check; the edited block is in the checked set (content/docs/kernel/contracts/data-engine.mdx:311), since theWriteObservabilityOptionsfence carries theos:checkmarkernode scripts/check-nul-bytes.mjs— greenScope notes
skip-changesetis the PM's to apply at acceptance.content/docs/releases/untouched. This page is hand-written and not routed tomerge=os-regen, so it is edited directly and never regenerated.data-engine.mdxEngineQueryOptionsblock omitssearchFields, declared on the schema since #4371 #7170 — the sameEngineQueryOptionsblock omitssearchFields, a declared member since [P2] A direct engine call silently dropssort/select/skip/populate— declared query contract, zero enforcement #4371. Different class (an omission, not a retired key still documented), so it was left for triage rather than ridden in here.Generated by Claude Code