Skip to content

feat(spec,objectql): IDataEngine.find/findOne accept the author state — engine fills SortNode.order's declared default (#6300) - #7269

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-6300-engine-author-state
Aug 10, 2026
Merged

feat(spec,objectql): IDataEngine.find/findOne accept the author state — engine fills SortNode.order's declared default (#6300)#7269
os-zhuang merged 2 commits into
mainfrom
claude/issue-6300-engine-author-state

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Premise (verified on fresh origin/main @ f40c5b4)

  • IDataEngine.find/findOne (packages/spec/src/contracts/data-engine.ts:168,183) still took EngineQueryOptionsParsed (z.infer), as ADR-0122 第二期:major 窗口翻转裸名为解析态语义 + XInput 去留 #6083 pinned them; ObjectQL.find/findOne matched.
  • The engine filled no default anywhere on the read path — the AST was built by bare spread, so admitting author state would have sent order: undefined to drivers.
  • count already took EngineCountOptions (z.input) — the asymmetry the card notes.
  • ADR-0122's flip had NOT already landed here. Premise holds; proceeded.

Phase-1 measured default census (parsed vs author state of EngineQueryOptionsSchema)

#KeyDeclared defaultRuntime consumer that assumes it present
1orderBy[].orderSortNodeSchema.default('asc') (query.zod.ts:64)None in the engineengine.ts never reads .order. Every driver coalesces absence to 'asc': sql-driver.ts:4864,4923,8574,8673, memory-driver.ts:1339, mongodb-driver.ts:664, mongodb-aggregation.ts:101, remote-transport.ts:1565. The only thing requiring parsed state was the type of QueryAST at AST construction.
2search.fuzzy / search.operator / search.highlightfalse / 'or' / false (query.zod.ts:417,418,422)None — all three are [EXPERIMENTAL — not enforced] (#4286); expandSearchToFilter reads only .query/.q (search-filter.ts:64), and expandSearchOnAst deletes search off the AST before middlewares/hooks/drivers see it.
3expand (nested QuerySchema, recursive)deltas 1–2 recursivelyNo driver reads ast.expand; the nested read re-enters engine.find (engine.ts expand path), which fills again.
contextExecutionContextSchema.partial().partial() erases the defaults' requiredness — no delta.
where/fields/limit/offset/topnone (filter.zod.ts pins "no .default(), no .transform()")no delta.

Driver-side status-quo measurement the card asked for: an order: undefined sort node behaves as 'asc' on every driver today — identical to the schema's declared default. So filling it changes no query's answer.

Bound verdict: the set is exactly SortNode.order + close kin → proceed (STOP guard not triggered).

Changes

FileChange
packages/spec/src/contracts/data-engine.tsfind/findOnequery param: EngineQueryOptionsParsedEngineQueryOptions (z.input, ADR-0122), with a doc note on the flip and the count symmetry restored.
packages/objectql/src/engine.tsSignatures flip to match. New fillQueryAstDefaults: runs each authored sort node through SortNodeSchema (recursively through expand) before the QueryAST is built — the schema's own defaulting parse, so declared defaults stay single-sourced in spec. search deliberately NOT parsed (measured: its defaults are read by nothing and the key is deleted from the AST unread; parsing would refuse the wire-tolerated comma-string search.fields shape pinned in query-expression-conformance.test.ts). Deletes the two as any on the nested-expand re-entry (fields/orderBy).
packages/objectql/src/engine-author-state-query.test.tsNew pins (9) — see reverse-verification table.
packages/objectql/src/engine-filter-array-lowering.test.ts, hook-input-shape-contract.test.tsThe casts/comments that NAME the find/findOne contract follow it (EngineQueryOptionsParsedEngineQueryOptions).
scripts/query-options-erasure-baseline.jsonRatcheted down (engine.ts 9 → 8); test surface unchanged at 256 — the new deliberate off-contract probes use the sanctioned as unknown as EngineQueryOptions spelling, which the ratchet doesn't count.
.changeset/engine-author-state-find.mdOne non-major changeset: @objectstack/spec minor, @objectstack/objectql minor.

Cost of the schema-parse choice, measured: one small-object SortNodeSchema.parse per authored sort node, only when orderBy is present. Behavior delta of that choice: for typed callers — none (pure widening; every previously-compiling call still compiles, z.infer values are valid z.input). For type-BYPASSING callers only: a malformed sort node (retired direction spelling, unknown key) is now refused with the schema's own prescription instead of silently dropped-or-honored per driver (one query, two orders — #4721's class; normalizeSortNodes already refuses it on the wire path). Stated in the changeset.

Reverse verification (direction predicted first)

Prediction written before execution: the defaulted spelling must behave as the schema's declared 'asc' — identical to explicit-asc, exact reverse of explicit-desc on a tie-free fixture.

PinDirection predictedObserved
find({orderBy:[{field}]}) uncast vs IDataEngineasc: Alpha,Beta,Gamma✅ equal
defaulted ≡ explicit 'asc'equal
defaulted ≢ explicit 'desc'exact reverse (Gamma,Beta,Alpha)
driver receives order:'asc', never undefined[{field:'name',order:'asc'}]
nested expand query filled on its own readperson read carries order:'asc'
findOne order-less orderBy is a legal #4419 predicatefirst-ascending row, limit:1
explicit order never clobbereddesc preserved on AST
direction spelling refused with rename prescriptionthrows /order/
unknown sort-node key refused by namethrows /frobnicate/

In-flight check (incl. #7178)

Gates

Special-inspection items

  1. search left unparsed is a measured decision, not an omission — first draft parsed it through FullTextSearchSchema and query-expression-conformance.test.ts went red: the wire path deliberately hands the engine search.fields as a comma-string (and the q spelling), which the schema refuses. The parse was dropped; the doc on fillQueryAstDefaults records both measurements. Reviewers should confirm they agree the type-level claim (covered by the helper's one documented cast, value deleted unread) is acceptable until EngineQueryOptionsSchema.search rejects the bare query string that ADR-0061 D1 calls canonical — so every engine caller that wants it must as any, losing the whole query's checking #7178's union lands.
  2. New refusal surface for as any callers (malformed sort nodes) — deliberate, per-schema strictness riding with the defaulting parse; if triage judges it must ride a major instead of a minor, only the changeset needs editing.
  3. The issue floated ADR-0122-flip work "should ride the next major"; the measured driver census (all drivers already default 'asc') is why this ships as a widening minor — flagged for the reviewer to confirm.

Closes#6300


Generated by Claude Code

… — engine fills SortNode.order's declared default (#6300)
ADR-0122's core argument — the first key an author writes must default
correctly — now holds on the engine's primary read entry. find/findOne's
query parameter flips from EngineQueryOptionsParsed (z.infer) back to
EngineQueryOptions (z.input), the author-state shape count already took,
and ObjectQL fills the one consumed default (SortNode.order → 'asc')
by running each authored sort node through SortNodeSchema before the
QueryAST is built — recursively through expand — so the declared default
stays single-sourced in packages/spec.
Phase-1 measured delta between the two states: orderBy[].order (the one
default anything consumes — every driver already coalesced its absence
to 'asc'), the three inert search flags (read by no executor, deleted
from the AST unread), and their recursion through expand. context is
.partial() (no delta); where/fields/limit/offset/top carry no defaults
or transforms.
Widening for typed callers; the one behavior change is for
type-bypassing callers only: a malformed sort node (retired 'direction'
spelling, unknown key) is refused with SortNodeSchema's own prescription
instead of silently dropped-or-honored per driver (#4721's class),
matching the wire path's normalizeSortNodes.
Closes#6300
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SaEx5461eovGX7AS9aLDV
@vercel

vercelBot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 10, 2026 5:37am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/objectql, @objectstack/spec.

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

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/objectql, packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via packages/objectql, @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/migration-from-objectql.mdx(via @objectstack/objectql)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/deployment/vercel.mdx(via @objectstack/objectql)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via packages/objectql, @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/permissions/authentication.mdx(via @objectstack/objectql)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/permissions/system-context.mdx(via packages/objectql, packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via packages/objectql, @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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.

@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 10, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 10, 2026 04:58
@os-zhuang
os-zhuang added this pull request to the merge queueAug 10, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Aug 10, 2026
…eline on the merged tree
Both #7249 and this PR ratcheted scripts/query-options-erasure-baseline.json;
the merged tree carries both reductions, so the file is re-measured with
--update rather than hand-merged (the resolution rule declared in the PR body).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SaEx5461eovGX7AS9aLDV
@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

Merge-queue ejection (MERGE_CONFLICT) resolved. The conflict was exactly the one declared in the PR body's in-flight section: #7249 merged and both PRs ratchet scripts/query-options-erasure-baseline.json. Applied the declared rule — merged origin/main and re-measured the baseline with pnpm check:query-options-erasure --update on the merged tree (now 67 non-test / 249 test sites, both PRs' reductions reflected; ratchet green).

Merged-tree verification before push: spec + objectql rebuild, objectql typecheck, and the three affected suites (author-state pins, findOne contract, query-expression conformance) — 145/145 green; the engine-side fill was written to #7249's search union and needed no change.

Not re-queueing myself (dispatch says no auto-merge from this seat) — ready for re-queue when the queue owner is.


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queueAug 10, 2026
Merged via the queue into main with commit 74155c7Aug 10, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6300-engine-author-state branch August 10, 2026 06:46
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IDataEngine.find/findOne 仍收 parsed 查询态 —— ADR-0122 之后它应当能收作者态,但那需要引擎真的补 SortNode.order 默认

2 participants

@os-zhuang@claude