test(driver-sql): pin the SORT axis' unprovisioned-injected-anchor premise (#10744) - #11165
Merged
os-zhuang merged 1 commit intoAug 22, 2026
Merged
Conversation
…0744) An ORDER BY over a registry-injected anchor on an ADR-0015 external object is refused by neither runtime door and dropped by the #3821 ladder, so the rows come back unordered under a success. That runtime fact is the entire warrant of `sort-field-unprovisioned` (#10474), an author-time WARNING, and nothing asserted it — so a future change at either door would leave the warning silently wrong with nothing red. The control leg (a real remote column, which reverses) is load-bearing: without it a driver that stopped sorting entirely would keep every anchor leg green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RfyXxZ2WPjcjhuXpiQQc3y
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. |
os-zhuang
marked this pull request as ready for review
August 22, 2026 23:23
Contributor
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32605126037 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
os-zhuang
deleted the
claude/issue-10744-sort-axis-unprovisioned-anchor-pin
branch
August 22, 2026 23:35
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 #10744
Test-only. One new file,
packages/drivers/driver-sql/src/sql-driver-external-unprovisioned-sort-anchor.test.ts. No runtime code is touched, and nothing here changes behaviour.What is pinned, and why it needs pinning
sort-field-unprovisioned(@objectstack/lint, shipped by #10474) is an author-time WARNING about a list view ordering by a registry-injected anchor on an ADR-0015externalobject. Its entire warrant is a runtime fact: neither SORT door refuses that ORDER BY, so the authoring gate is the only door there is. Nothing in the repo asserted that fact, so if a door ever started refusing it, the warning would silently become wrong — describing a silent degradation that no longer happens, on a path that is now a loud400, at the wrong severity — with nothing turning red.Both doors narrow on
formulaalone, which is why an injecteddatetime/lookupanchor sails through:assertSortFieldsExist(@objectstack/metadata-protocol,protocol.ts, A non-dottedorderBynaming aformulafield answers 200 in arbitrary order — the sort is silently dropped (measured on driver-sql + driver-memory) #6994) — third verdict filtersUNMATERIALIZED_SORT_TYPES, which isformulaalone. Re-read onmainfor this PR: unchanged.assertOrderByIsMaterializable(@objectstack/objectql,engine.ts,engine.find()still drops aformulaORDER BY silently — decide whether the engine refuses or keeps its internal-caller tolerance #7095) —schema.fields[f]?.type === 'formula'. Re-read onmainfor this PR: unchanged.Neither file is edited here; both were read only.
packages/drivers/driver-sqlsits below both in the dependency graph and cannot import either door, so this file pins the half that is observable from the driver — the ORDER BY reaches the driver and the driver answers success with the sort dropped — plus the type half, asserted from the spec's own injected definitions (no anchor is aformula), which is the property both doors narrow on.Measured on this file's own fixture
A real
SqlDriverover better-sqlite3 inschemaMode: 'external', against a remotecustomerstable carrying exactly[id, name, email, region, lifetime_value]and none of the seven injected anchors:…and the same for the other five anchors (
organization_id,updated_at,created_by,updated_by,owning_business_unit_id), which the test derives fromunprovisionedInjectedColumnsrather than typing out, so a future ADR that adds or removes an anchor is covered automatically.One case records the mechanism from the emitted SQL, because "the sort was never issued" and "the sort was issued and dropped" are different facts:
So the ORDER BY does reach the remote database, is rejected there, and is dropped by the #3821 recovery ladder, which returns the rows unordered under a success. (knex quotes identifiers with backticks on this stack, so SQLite raises rather than degrading the name to a string literal the way a double-quoted one would — the #5348 shape. Both routes end at the same answer; this is the measured one.)
Why the baseline leg is load-bearing
asc === descon its own is satisfied by a driver that stopped sorting entirely — a far larger defect that would leave every anchor case green. The reversing control on a real remote column is what makes the anchor result a dropped sort rather than a coincidence. The card and triage both said the anchor legs must not ship without it, and the file's header says so too, with a⛔ Do not delete the control to "simplify" this filebeside the case.That is not an assertion about the control — it was measured. See the second reverse-verification leg below: with the control's column made unresolvable (simulating "no sort is ever applied"), only the control goes red and all seven anchor legs stay green.
What a future red here means — the reason for the long header
A premise pin fails in an unusual direction, and the file header states it so a future reader does not misread it:
sort-field-unprovisionedand delete the cases its warrant needed — not to restore the behaviour this file documents.A premise pin whose red is misread as a regression gets "fixed" by putting back the very defect it records. Same posture, for the same reason, as
packages/objectql/src/engine-external-tenant-scope.test.ts(#7738), the precedent this card names.Fixture notes
examples/app-showcase/src/data/objects/external/customer.object.tsdeclares it, copied rather than imported: a test that reads outside its own package is invisible to turbo's affected set and to thetesttask's input hashing. It is a fixture of that shape, not a mirror of that file.applySystemFieldsbuilds it — the author's fields plus the spec's owninjectedSystemColumnDefs.Engine.syncObjectSchemahandsregisterExternalObjectthe post-injection registry object, so this is what the driver really receives for a federated object.initObjects, so its column set is exactly the card's and carries nothing the platform would have injected. TheFIXTUREcase asserts that against the livecolumnInfo().SqlDriveris used), so theassertEngineUpdateDispatch/assertEngineDeleteDispatchrequirement does not arise.check:engine-double-contractran green anyway.Verification — union re-run at
7059f30d, clean treeReverse verification, both legs mutating the fixture only (the runtime is untouched), each under a restoring
trap, each with the mutation confirmed on disk by grep counts before the run:created_at(i.e. "someone fixed the runtime")3 failed | 8 passed—FIXTUREred,ORDER BY created_atred (descnow reverses),MECHANISMred (one statement, no ladder retry). Control and the other six anchors green.1 failed | 10 passed— onlyCONTROL a real remote column IS sortedred.Gates, all at
7059f30d:pnpm --filter @objectstack/driver-sql exec vitest run—Test Files 108 passed | 5 skipped (113),Tests 1806 passed | 72 skipped (1878)pnpm --filter @objectstack/driver-sql typecheck(tsc --noEmit) — cleanpnpm lint(eslint . --no-inline-config, whole repo) — cleannode scripts/pm/dispatch-gates.mjs:check:driver-conformance,check:slot-lookup,check:test-source-alias,check:type-source-resolution,check-ci-filter-parity.mjs,check-plugin-teardown-shape.mjs,docs-audit/check-affected-docs.mjs— all greencheck:query-options-erasure,check:type-check-coverage,check:engine-double-contract,check:where-matcher— all green. The erasure ratchet was red on the first pass (test surface grew 240 → 242) and was fixed the way it prescribes — the twofindcalls now build a typedDriverQuerylocal instead ofas any; the ratchet is back at240 site(s) in 47 file(s) — at the ceiling,none new.check:nul-bytes,check:cross-package-test-inputs— greenNo changeset: this PR is test-only and publishes nothing, so it carries
skip-changeset(this repo's mechanism for that; an empty-frontmatter changeset is refused here bycheck-empty-changeset.mjs).Generated by Claude Code