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)

# Key Declared default Runtime consumer that assumes it present
1 orderBy[].order SortNodeSchema .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.
2 search.fuzzy / search.operator / search.highlight false / '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.
3 expand (nested QuerySchema, recursive) deltas 1–2 recursively No driver reads ast.expand; the nested read re-enters engine.find (engine.ts expand path), which fills again.
context ExecutionContextSchema.partial() .partial() erases the defaults' requiredness — no delta.
where/fields/limit/offset/top none (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

File Change
packages/spec/src/contracts/data-engine.ts find/findOne query param: EngineQueryOptionsParsedEngineQueryOptions (z.input, ADR-0122), with a doc note on the flip and the count symmetry restored.
packages/objectql/src/engine.ts Signatures 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.ts New pins (9) — see reverse-verification table.
packages/objectql/src/engine-filter-array-lowering.test.ts, hook-input-shape-contract.test.ts The casts/comments that NAME the find/findOne contract follow it (EngineQueryOptionsParsedEngineQueryOptions).
scripts/query-options-erasure-baseline.json Ratcheted 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.md One 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.

Pin Direction predicted Observed
find({orderBy:[{field}]}) uncast vs IDataEngine asc: 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 read person read carries order:'asc'
findOne order-less orderBy is a legal #4419 predicate first-ascending row, limit:1
explicit order never clobbered desc preserved on AST
direction spelling refused with rename prescription throws /order/
unknown sort-node key refused by name throws /frobnicate/

In-flight check (incl. #7178)

Gates

  • packages/spec build (before any dist-derived check, per gen:api-surface / gen:export-origins read the dist without asserting it is FRESH — a stale dist launders a phantom breaking removal into the committed baseline #7122) ✅
  • check:generated — all 11 artifacts up to date ✅
  • check:api-surface — public API surface + factory signatures unchanged (exports did not change → no dual-snapshot needed) ✅
  • check:spec-parsed-alias (ADR-0122 convention) ✅
  • check:engine-double-contract
  • check:query-options-erasure — self-test + ratchet hold; baseline ratcheted down and committed ✅
  • turbo run test typecheck for @objectstack/spec + @objectstack/objectql + @objectstack/runtime — 35/35 tasks green (objectql 2915 tests, spec + runtime suites, all typechecks) ✅
  • Heavy verification serialized under /tmp/os-heavy-verify.lock

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

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 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-actions github-actions Bot 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 queue Aug 10, 2026
@github-merge-queue
github-merge-queue Bot 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

Copy link
Copy Markdown
Contributor Author

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 queue Aug 10, 2026
Merged via the queue into main with commit 74155c7 Aug 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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants