Skip to content

feat(objectql): a conformance kit a plugin driver outside this repository runs in its own CI - #27798

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-yyuer7
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-yyuer7

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread
Part of #27744

Clause-②: yes
Metadata-protocol: none

维护者速读

  • 改了什么: @objectstack/objectql 新增并导出一套插件驱动一致性测试套件(conformance kit):仓库外的驱动(如闭源 Salesforce 驱动)在自己的 CI 里调用它,用 spec 的驱动一致性用例表检验引擎对插件驱动的兜底契约(读、筛选下推或兜底、计数、写入拒绝、故障应答、introspectSchema 形状)。
  • 为什么改: 按 22:01Z 的决定,厂商专属驱动不进本仓;本仓接口一变,只有这套用例能让那边的 CI 及时变红。
  • 风险与回滚: 纯新增导出,不改任何现有行为与类型;回滚即 revert 本 PR。
  • 先看: packages/objectql/src/plugin-driver-conformance.ts(判定规则在 judgeRows / judgeReadThrow / flooredRead);packages/objectql/src/plugin-driver-conformance.test.ts(每个变异驱动应在哪条用例上失败)。
  • 两分钟验证: pnpm --filter @objectstack/objectql exec vitest run src/plugin-driver-conformance.test.ts,应见 330 个用例全绿(其中套件形态注册了约 300 个逐例测试)。
  • 需要你做的: 无,席位落地。

What changes

@objectstack/objectql exports a conformance kit for plugin drivers (driver.NAME services a server plugin publishes) that live outside this repository. The driver's own CI runs it; a change to the contract here then turns that CI red.

  • The contract it asserts is the engine's correctness floor over a plugin driver (external-driver-floor.ts), against the spec's driver conformance tables, never any one driver:
    • floor/shape: per fixture, the plainest read the floor sends returns every stored row once, string ids, no _id, and every stored value unchanged (a null stays null, a number stays a number, an instant may come back as a native Date).
    • floor/filter: every row of FILTER_LOGIC_CASES, the evaluated rows of FILTER_TEXT_CASES and every TEMPORAL_CASES filter (lowered as the engine lowers it, and again through its relative tokens). The driver's own answer to the query the floor sends must hold every matching row and only stored rows, or refuse with 400, 404 or 422; the floored answer must equal the table's expected.
    • floor/count: count through the floor, per FILTER_LOGIC_CASES row.
    • floor/order: the floor's paged walk for every PAGINATION_CASES sort is a partition in the requested order.
    • floor/write: caller-named write probes; a refused one must reach the caller as 422 EXTERNAL_WRITE_REFUSED.
    • floor/outage: with the caller's unavailable remote, find, findOne and count reach the caller as 503 ERR_DATASOURCE_UNAVAILABLE, never as rows and never as a 4xx.
    • floor/introspect: the IntrospectedSchema shape when the driver offers introspectSchema().
  • Pushdown is measured, not declared. DriverCapabilities carries no pushdown claim (retired in 17.0.0), so each read result carries pushdown: exact | superset | refused and each ordered read order: honoured | not-honoured.
  • An opt-in direct tier checks that the driver answers the whole query itself: exact filter, count, findOne, deterministic paged walks (PAGINATION_CASES, PAGINATION_UNORDERED_CASES) and limit by presence (PAGINATION_ZERO_LIMIT_CASES). The floor needs none of it. It is the oracle the epic's P3 verified-pushdown credential can key on (case ids are stable: tier/group/fixture/case name).
  • Runner-agnostic. definePluginDriverConformanceSuite({ describe, it, afterAll }, options) registers one test per case (vitest or jest); runPluginDriverConformance(options) answers a report with no framework; createPluginDriverConformance(options) hands out the raw cases. setup(fixture) builds the driver over a remote holding exactly the fixture's rows: a fake of the remote's API in front of the driver's real translation code, or a sandbox seeded from pluginDriverConformanceFixtures(). Reads share one setup per fixture; each write probe gets a fresh one; every setup is torn down once. Absent optional inputs (setupUnavailable, writeProbes, introspectSchema) answer not-offered, never a silent pass.

Why

Epic #27261 P2, maintainer order 2026-10-29T22:01Z 「同意」: vendor-specific drivers stay out of this repository as plugin packages, verified by a public conformance kit. Until now the floor's contract was pinned only by this package's internal tests, which a driver in another repository never runs.

Tests

packages/objectql/src/plugin-driver-conformance.test.ts holds the kit to both halves of being an instrument:

  • Batteries pinned by count against the spec tables (one case per table row per tier), unique ids, no setup before a case runs, unknown tier refused.
  • Passes: an exact driver (both tiers, every read exact, every order honoured); an every-row driver (the guide's step 1: floor tier passes, measured superset and not-honoured, direct tier fails); a driver refusing $or with status 400 (floor passes, measured refused); a remote answering instants as native Dates; absent optional inputs reported not-offered; teardown once per setup.
  • Each broken rule fails on the case that names it: dropping a branch of an $or; a null stored as ''; a number stored as text; a refusal with no status; a row answered twice; an invented row; an outage answered as [] (all three reads, a count of 0 included) or as a 404; a write refusal declaring 500; a probe whose outcome differs either way; introspectSchema spelling isPrimary; direct tier: a count ignoring the filter, a paged walk with no stable order.
  • The suite form (definePluginDriverConformanceSuite under this file's own vitest describe / it / afterAll) runs the exact driver through both tiers, as a vendor's CI would.

Measured at head 07def8cc (origin/main 968cacfd merged; the merge touched nothing in @objectstack/objectql's dependency closure):

pnpm --filter @objectstack/objectql test — 491 files / 9574 tests passed
 Test Files  491 passed (491)
      Tests  9574 passed (9574)
os-verify-lock: VERDICT command-exit 0 · held the lock 299s

The new file alone: Tests 330 passed (330).

pnpm --filter @objectstack/objectql typecheck — exit 0
check:test-typecheck: OK — @objectstack/objectql's test layer compiles under packages/objectql/tsconfig.test.json; 39 file(s) / 233 error(s) / 64 pinned signature(s) held in test-typecheck-debt.json

No new test-typecheck debt.

Acceptance notes

Verification

dispatch-gates --commands at 07def8c: 59 derived, 59 run, all exit 0

Every derived family run locally, exit codes captured before any pipe. check:dual-build-cjs-loads first exited 3 (no built dist/ yet); rerun after check:type-check-debt built the packages: exit 0.

Run reconciliation — 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 59 derived famil(ies) accounted for — 59 run, 0 NOT-MEASURED (a DERIVED zero — all 59 recorded an exit code and none of them is 3).

The test-double ratchets read the new file: check:objectql-double-limit (none new), check:where-matcher (none new), check:engine-double-contract exit 0.

The remaining families are CI's (the PR's check-runs).

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No spec change.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; eslint --no-inline-config over the three changed source files exits 0 (the repo-wide pnpm lint is CI's run).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: not touched.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the maintainer summary is Chinese by repo rule).
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_017ihL2g15Dr6sEsgv4sGBkq


Generated by Claude Code

@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql, touching 60 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/objectql/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 38651598a7d8e72151ddedde3eb02e4fb0c207b6.

⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/objectql/src/index.ts) — pages documenting those are invisible to this run
  • 26 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 38651598a7d8e72151ddedde3eb02e4fb0c207b6 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 9a70c771c3905ce86de79963730204d7fb3fcb13 — the merge of head eb7990be468b9b2c888d52098d7a07c6c766eb05 into base 38651598a7d8e72151ddedde3eb02e4fb0c207b6, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9a70c771c3905ce86de79963730204d7fb3fcb13 && git checkout 9a70c771c3905ce86de79963730204d7fb3fcb13
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 38651598a7d8e72151ddedde3eb02e4fb0c207b6 eb7990be468b9b2c888d52098d7a07c6c766eb05 && git checkout -B drift-repro 38651598a7d8e72151ddedde3eb02e4fb0c207b6 && git merge --no-ff eb7990be468b9b2c888d52098d7a07c6c766eb05

node scripts/docs-audit/affected-docs.mjs --json 38651598a7d8e72151ddedde3eb02e4fb0c207b6

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 38651598a7d8e72151ddedde3eb02e4fb0c207b6 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (HTTP 429 rate_limit, 2026-10-29T23:39Z, same refusal as 6511103853 on #27788) · head 07def8c

The seat's at-tier review call was refused by the model quota, and per contract-review.md the tier is not lowered. Owed on this head: one same-shape CONTRACT_REVIEW_TIER record. Face: Clause-②: yes — a new exported surface of @objectstack/objectql (createPluginDriverConformance / runPluginDriverConformance / definePluginDriverConformanceSuite / pluginDriverConformanceFixtures) that out-of-repo plugin drivers will run in their own CI, asserting #27277's floor contract. No packages/spec/src/** edit.

PM ruling on the PR's open question: this PR closes #27744 (Fixes). Running the kit from driver-odata goes to whichever of #27741 (PR #27796) / this PR lands second; the guide pointer goes to the guide card #27747.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 07def8ccc7c559ac98a6b10b2201012de97d2f70 · 2026-10-30T00:02Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 07def8ccc7c559ac98a6b10b2201012de97d2f70
Local-runs: none

Face: the Clause-②: yes declaration alone (PR body and changeset, both line-initial). The PR's committed file list is four paths — .changeset/27744-plugin-driver-conformance-kit.md, packages/objectql/src/index.ts, packages/objectql/src/plugin-driver-conformance.ts, packages/objectql/src/plugin-driver-conformance.test.ts — +1424/−0; no packages/spec/src/**, no governed register row, no (narrowing) arm, so no schema, rules or narrowing face and no ④. Inputs: card #27744 (body, claim 6510468826, os-dev-report 6511097820), PR #27798 body, file list and net diff against main at the head, the 53 check-runs on the head, and the PR comments (docs-drift 6511094573, handoff 6511123974, claim 6511469408). The brief named card #27741; that is the driver-odata card (PR #27796), which this PR's body, claim and handoff do not name as its card — #27744 is. Both were read; #27741 contributes nothing to this head beyond the wiring item answered in ③.

① Derived judgments

  1. Public surface widened on @objectstack/objectql's root entry (exports["."], dist/index.*): six value exports — createPluginDriverConformance, runPluginDriverConformance, definePluginDriverConformanceSuite, pluginDriverConformanceFixtures, PLUGIN_DRIVER_CONFORMANCE_FIXTURE_NAMES, PluginDriverConformanceFailure — and fifteen types, all re-exported from the new module. The changeset's "New exports" list names exactly these. Clause-②: yes is the right declaration for it. Right.
  2. No accept set moves. The index.ts hunk is additions only; no existing export is renamed, removed or re-typed. external-driver-floor.ts, external-driver-errors.ts, driver-query-in-memory.ts and driver-connect-errors.ts are read by the kit and not touched, so the engine's floor over a plugin driver behaves at this head exactly as on main. The changeset's "Nothing existing changes" holds. Right.
  3. The kit asserts the floor's actual contract, read against the head tree's external-driver-floor.ts and external-driver-errors.ts: the floor sends the driver where, orderBy and limit: EXTERNAL_ROW_CAP + 1, never offset or fields, in ONE inner.find per read — so flooredRead reading answers[0] is the whole answer, shapeCase's { limit: ROW_LIMIT } is the plainest read the floor sends, and floorOrderCase's per-page mustInclude = all is what the floor needs. A driver throw with a status in EXTERNAL_READ_REFUSED_STATUSES (400, 404, 422) reaches the caller as ExternalReadRefusedError; the kit's judgeReadThrow plus the instanceof ExternalReadRefusedError check match that door. Any other read throw is DatasourceUnavailableError (503, reason remote); the outage case's instanceof DatasourceUnavailableError matches. A write throw with a status in EXTERNAL_WRITE_REFUSED_STATUSES is ExternalWriteRefusedError with code === EXTERNAL_WRITE_REFUSED_CODE and status 422; writeCase matches. The PR body's "refuse with 400, 404 or 422" and "422 EXTERNAL_WRITE_REFUSED" / "503 ERR_DATASOURCE_UNAVAILABLE" are the floor's own numbers. Right.
  4. The oracle is the spec's published tables, imported through the @objectstack/spec/data barrel (export * of filter-logic-conformance, filter-text-conformance, temporal-conformance, pagination-conformance and filter-lowering). The fixture field maps match the row shapes: filter_logic a/b/c/d/owner/status/parent_object/parent_id, filter_text name/score, pagination status/rank/name, temporal at (datetime) / on (date) — the temporal cases filter only at (33 leaves) and on (9), so isDatetimeColumn: column === 'at' is the correct declared-type reader and dropping writerForm / why from TEMPORAL_ROWS drops spec-authoring metadata, not columns. Right.
  5. Declared non-coverage is real and named in both places (PR body and module comment): FILTER_TEXT_CASES rows with expectRejection: true are refused by the engine's door before a driver is asked, so they test the engine; TEMPORAL_TIME_CASES has no comparand class in the floor yet. Skipping them is a truthful gap, not a silent one. Right.
  6. Pushdown measured, not declared. DriverCapabilitiesSchema's TSDoc on main and the semantic migration entry 17.driver-capabilities-inert-bits-removed confirm the query bits are gone, so the card's "supports-declared pushdown" premise could not be implemented as written; the kit's per-case exact | superset | refused plus order: honoured | not-honoured is the only honest reading and is what the P3 credential can key on. The premise correction is right.
  7. No new dependency edge. resolveFilterTokens comes from @objectstack/core, already a runtime dependency of @objectstack/objectql and already imported by ten non-test modules reachable from index.ts; @objectstack/core does not depend on @objectstack/objectql. No cycle, nothing added to package.json. Right.
  8. introspectSchema shape is judged against IntrospectedSchema in packages/spec/src/contracts/schema-diff-service.ts: dialect string, introspectedAt ISO 8601, tables record, column name / type / nullable / primaryKey, indexes optional with absence meaningful — the kit checks indexes only when the key is present, which is the contract's own rule. Right.
  9. Runtime strings carry no tracker number. Every PluginDriverConformanceFailure message a vendor author reads names the query, the rule and the fix; the only #NNNN tokens in the new file are in comments. The // [#27744] in index.ts is a code comment in the file's existing convention. Right.
  10. Floor memoisation is used correctly. floored() wraps a fresh recording Proxy per read so createExternalDriverFloor's per-driver WeakMap never hands back a stale floor; writeCase floors the raw driver of a fresh open() per probe. Right.
  11. Test file: batteries pinned by count against the spec tables per tier, unique case ids, lazy setup, unknown tier refused, four passing driver shapes, eleven mutants each pinned to the case id that names the broken rule, teardown-once, and the suite form registered under vitest's own describe / it / afterAll. Test Core (8 shards) is green on the head. Right.
  12. Observation, not a finding: floorOrderCase labels every order case pushdown: 'exact' because the floor sends it no where; the measurement that case carries is order. Harmless, and the report shape is documented.

② Semver level

  • Changeset .changeset/27744-plugin-driver-conformance-kit.md: '@objectstack/objectql': patch, body carrying Clause-②: yes and Metadata-protocol: none, both line-initial; the PR body carries the same two lines. Consistent with the diff: the diff publishes new exports from a released package (so not skip-changeset), adds no authorable key, value or type (so the kind is none).
  • Level rule (AGENTS.md Post-Task Checklist step 3; check-changeset-no-major.mjs level axis, maintainer ruling A of 2026-10-21): the level follows the metadata-protocol kind alone — none takes patch, and a non-protocol widening ships patch. So patch beside Clause-②: yes / Metadata-protocol: none is the right level. Check Changeset concluded success on the head, the gate's own reading of the same lines. @objectstack/objectql sits in the fixed group, so the lockstep bump is patch for the group — consistent.
  • Clause-②: line: yes, no arm — right for a widening of the public surface with no narrowing. No Skills-docs: line is owed (kind none, no (narrowing) arm). No BREAKING banner and no ADR-0087 disposition owed. Dogfood battery n/a (no ! in the title, no narrowing arm) — right.

③ Boundary flags

  1. open_questions[0] (os-dev-report 6511097820): close objectql: publish a driver conformance kit that out-of-repo plugin drivers run in their own CI, so a contract change here is caught there — epic #27261 P2 #27744 with this PR, or keep Part of until driver-odata runs the kit. Answered by the PM ruling in PR comment 6511123974: this PR closes objectql: publish a driver conformance kit that out-of-repo plugin drivers run in their own CI, so a contract change here is caught there — epic #27261 P2 #27744 (Fixes); the driver-odata wiring goes to whichever of driver-odata: a declarative OData v4 datasource driver (plugin-published id, pushdown for $filter/$orderby/$top/$skip/$count, the engine floor for the rest) — epic #27261 P2 #27741 (PR feat(driver-odata): a declarative OData v4 datasource driver, published as a plugin driver id behind the engine floor #27796) / this PR lands second; the guide pointer goes to docs: the external-systems guide gains the OData datasource section and the three-way choice (declarative OData, plugin driver, DataSync mirror) — epic #27261 P2 #27747. At this head the PR body still reads Part of #27744; under the option the ruling picked, the body edit is the owning seat's act (the dev's option A names the PM as the editor), the PR-body guards check is success on the current body, and a body edit moves no byte of the reviewed tree, so this record stands across it. Owning seat: edit the body to Fixes #27744 before landing. Not a must-fix.
  2. Acceptance note — premise correction (supports-declared pushdown does not exist): answered right, ① item 6.
  3. Acceptance note — not covered, deliberately (text rejection rows, TEMPORAL_TIME_CASES): accepted, ① item 5; a vendor reads the gap in the module comment.
  4. Acceptance note — no new package: the card allowed @objectstack/objectql or a small test-kit package; placing the kit beside createExternalDriverFloor so a vendor tests against the floor of the version it installs is the better reading, and it stays inside the claim's file surface (packages/objectql/src/**). Accepted.
  5. out_of_scope_findings (two, carriers named, not filed): the driver-odata wiring (carrier driver-odata: a declarative OData v4 datasource driver (plugin-published id, pushdown for $filter/$orderby/$top/$skip/$count, the engine floor for the rest) — epic #27261 P2 #27741) and the guide pointer in content/docs/data-modeling/virtual-objects.mdx (carrier docs: the external-systems guide gains the OData datasource section and the three-way choice (declarative OData, plugin driver, DataSync mirror) — epic #27261 P2 #27747) — both placed by the PM ruling above. Answered.
  6. Card task item 2 ("driver-odata runs it in this repository as the first consumer") is not in this diff by the card's own serial constraint ("do not edit its package. Wire it in a later PR, or say in the report how the two meet"); the report says how they meet. Answered by the ruling.
  7. deviations: none declared in the objectql: publish a driver conformance kit that out-of-repo plugin drivers run in their own CI, so a contract change here is caught there — epic #27261 P2 #27744 report; the file surface held (four paths, all inside packages/objectql/src/** and .changeset/**). PR author is objectstack-fleet[bot] (relay write) — no non-relay login to name. Changed lines 1424, under the 3000 threshold. No governed path.
  8. Check-runs on the head (53): every gate-carrying context success — Lint & Repo Gates, TypeScript Type Check, Test Core and its 8 shards, Build Core, Governed Surface Queue Guard, Test UI, Live E2E UI, Dogfood Regression Gate, Check Changeset, Check PR Size, PR-body guards, Flag docs affected by code changes; the rest skipped by paths filter (UI build/lint/type/test shards, docs build, opt-in harnesses, live temporal conformance). Zero failures. The docs-drift comment is advisory and names no hand-written page this diff falsifies (the kit is new; the guide pointer is docs: the external-systems guide gains the OData datasource section and the three-way choice (declarative OData, plugin driver, DataSync mirror) — epic #27261 P2 #27747's).
  9. Escalation — the brief's card number. The dispatch brief named card driver-odata: a declarative OData v4 datasource driver (plugin-published id, pushdown for $filter/$orderby/$top/$skip/$count, the engine floor for the rest) — epic #27261 P2 #27741 for a PR whose body, claim and handoff name objectql: publish a driver conformance kit that out-of-repo plugin drivers run in their own CI, so a contract change here is caught there — epic #27261 P2 #27744. Read both; judged against objectql: publish a driver conformance kit that out-of-repo plugin drivers run in their own CI, so a contract change here is caught there — epic #27261 P2 #27744. The seat that writes the next brief for this lane should carry the PR's own card.

Must-fix: none.

Implemented-by: claude/project-thread-yyuer7
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

…-process OData service

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g15Dr6sEsgv4sGBkq
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head eb7990be468b9b2c888d52098d7a07c6c766eb05 · 2026-10-30T02:02Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: TIER_DEFAULT
Head-sha: eb7990be468b9b2c888d52098d7a07c6c766eb05
Local-runs: none

Delta review on record 6511619545 (head 07def8ccc7c559ac98a6b10b2201012de97d2f70, PASS, declaration face), by the same reviewer. Face unchanged: the Clause-②: yes declaration alone (PR body and changeset, both line-initial); no packages/spec/src/**, no governed register row (packages/drivers/** is not one), no (narrowing) arm, so no schema, rules or narrowing face and no ④. Inputs: card #27744 (body, claim 6510468826, os-dev-report 6511097820, claim amendment 6512765336), PR #27798 body, file list and net diff against main at the head (five paths, +1488/−0), the 48 check-runs on the head, and the PR comments (docs-drift 6511094573 as re-run on this head, handoff 6511123974, claims 6511469408 and 6513131440, record 6511619545).

What moved since the previous record — two commits. (a) beacfa67 merges origin/main at 0a4b0fb2 (38 commits, #27796 driver-odata among them) into the branch: the PR's four paths are byte-identical on both sides of the merge (a diff of 07def8cc..beacfa67 restricted to them is empty), and the merge's diff against 0a4b0fb2 is exactly those four paths at +1424/−0, so the merge carries nothing of the PR's own and nothing of main's is re-judged here — it is main. In the kit's dependency closure main moved packages/objectql/src/engine.ts (8 lines), secret-fields.ts (4) and external-driver-floor.test.ts (a test); external-driver-floor.ts and external-driver-errors.ts, the contract the kit asserts, did not move, and the spec tables the kit reads did not move. (b) eb7990be adds one file, packages/drivers/driver-odata/src/odata-driver.conformance-kit.test.ts (+64), which runs the exported kit over the OData driver and the in-process OData v4 service that #27796 landed. Sections the move does not touch stand as judged in record 6511619545, named below.

① Derived judgments

  1. Public surface of @objectstack/objectql: the index.ts hunk (six value exports, fifteen types), plugin-driver-conformance.ts and plugin-driver-conformance.test.ts are byte-identical to the head record 6511619545 judged. Items 1–12 of that record's ① stand as judged, including the premise correction (pushdown measured, not declared) and the declared non-coverage.
  2. No new public surface, no accept-set move. The new file is a *.test.ts under packages/drivers/driver-odata/src/; @objectstack/driver-odata ships files: ["dist", "README.md", "CHANGELOG.md"] and its exports point at dist/index.*, so the package's published surface is what main publishes. No source module of either package changes. Right.
  3. The new test calls the kit as the kit is exported. definePluginDriverConformanceSuite({ describe, it, afterAll }, options, title) — the api object matches PluginDriverConformanceTestApi (describe, it, optional afterAll), the third positional title exists with that meaning, and setup may answer a PluginDriverConformanceTarget synchronously (Target | Promise of Target); { driver } with object absent defaults to fixture.object, which registerExternalObject({ name: fixture.object, external: { remoteName: 'Rows' } }) binds to the service's one entity set. tiers absent ⇒ ['floor']; the header's reason for leaving the direct tier out is the driver's own design (count refuses a where it cannot translate whole; $filter is a superset by rule), so the opt-in tier would fail by design, not by defect. Right.
  4. The EDM map is total over the kit's field types. Record of PluginDriverConformanceFixture['fields'][string]['type'] to EdmType — the kit's PluginDriverConformanceField.type is exactly text | number | datetime | date, and the map names all four; a fifth type added to the kit later fails tsc here, which is the "fails in this repository first" the header promises. Read against the service's comparable: Edm.Double compares as a number, Edm.DateTimeOffset through Date.parse, Edm.Date lexically on ISO text — each consistent with the spec rows and with the kit's shape case (a number stays a number; an instant comes back as the stored string). Right.
  5. The key is right for the floor's row contract. The first ServiceProperty is id (Edm.String, nullable: false), which the service's $metadata declares as the key; the kit's fixture rows carry a string id on every row. In the driver's bind, the property named id maps to field id, so idIsKeyAlias is false, toRemote sends id on a create, and idOf answers the key's text — string ids, no _id, which judgeRows requires. Right.
  6. Paging is exercised, not assumed. serverPageSize: 5 makes every floor read (limit: EXTERNAL_ROW_CAP + 1, so $top is set) span several server pages; the driver follows @odata.nextLink through followLink (same origin as CONFORMANCE_ROOT) until limit rows or no link. Right.
  7. Both collations are the kit's intended use. Under the case-insensitive service the driver's eq / contains on text widen, the floor narrows, and the kit records superset while asserting the floored answer equals the table — the "never narrower" rule of filter.ts made checkable by a consumer that is not the driver's own test. Right.
  8. Outage probe reaches the right door. setupUnavailable hands the driver a fetch that throws TypeError; the transport rethrows an Error with no status (could not be reached), the floor's remoteFailure answers DatasourceUnavailableError (503), and the first request of every verb is $metadata, so find, findOne and count all hit it. outageCase checks instanceof DatasourceUnavailableError. Right.
  9. Write probes are judged against the service's actual answers, default fixture filter_logic (rows 1–4): a create of { id: '1', a: 'x' } posts { id: '1', a: 'x' } (item 5), the service answers 409 (An entity with this key already exists.), 409 is in EXTERNAL_WRITE_REFUSED_STATUSES (400, 404, 409, 412, 422), the floor throws ExternalWriteRefusedError with EXTERNAL_WRITE_REFUSED_CODE, and writeCase passes refused — the inline comment names the mechanism that fires. The update '1', { a: 'changed' } patches Rows('1'), which exists, answers 200 with the entity, and passes accepted. The kit opens a fresh setup per probe, so the create's 409 and the update touch no shared read session. Right.
  10. Introspect probe. The driver offers introspectSchema(), built from the service's own $metadata (metadataFor): tables.Rows with name / type / nullable / primaryKey per column, dialect: 'odata-4.0', introspectedAt ISO, no indexes key — the kit checks indexes only when present. Right.
  11. Not a duplicate of odata-driver.conformance.test.ts (feat(driver-odata): a declarative OData v4 datasource driver, published as a plugin driver id behind the engine floor #27796): that file runs the spec case-sets through the ObjectQL engine with the driver on an external datasource; this one runs the exported kit's own floor over the raw driver — the first in-repo consumer of the kit, which is the card's second task item. No test is deleted. Right.
  12. Gates the new file is subject to. @objectstack/objectql is imported by package name, a devDependency (workspace:*) already on main with the vitest alias to ../../objectql/src/index.ts (check:test-source-alias form); every relative import is same-package, so check:cross-package-test-inputs has nothing to see; the package's tsconfig.json includes src/**/*, so tsc --noEmit compiles the test (TypeScript Type Check green). No runtime string in the file carries a tracker number; the file carries none anywhere. The commit's trailers are the model-free pair. Right.
  13. Observation, not a finding: nullable is unset on the non-key properties, which metadataFor writes as Nullable="true" — right for the fixtures (d: null rows), and immaterial to the kit, which reads nullability only through introspectSchema()'s shape.

② Semver level

Stands as judged in record 6511619545: the changeset .changeset/27744-plugin-driver-conformance-kit.md is byte-identical ('@objectstack/objectql': patch, Clause-②: yes, Metadata-protocol: none, both line-initial, mirrored in the PR body), and the move changes nothing it describes. The new path is a test in a package that ships dist only, so no @objectstack/driver-odata entry is owed and skip-changeset is still wrong (the kit publishes). Check Changeset concluded success on this head. Clause-②: line: yes, no arm — right; no Skills-docs: line, BREAKING banner or ADR-0087 disposition owed; dogfood battery n/a.

③ Boundary flags

  1. open_questions[0] (os-dev-report 6511097820) — further settled since the previous record: the claim amendment 6512765336 records that driver-odata: a declarative OData v4 datasource driver (plugin-published id, pushdown for $filter/$orderby/$top/$skip/$count, the engine floor for the rest) — epic #27261 P2 #27741 landed first (PR feat(driver-odata): a declarative OData v4 datasource driver, published as a plugin driver id behind the engine floor #27796, 0a4b0fb2), so under the claim's serial rule this card adds the driver-odata test, which eb7990be does; PR feat(objectql): a conformance kit a plugin driver outside this repository runs in its own CI #27798 closes the card (Fixes, ruling 6511123974); the guide pointer stays with docs: the external-systems guide gains the OData datasource section and the three-way choice (declarative OData, plugin driver, DataSync mirror) — epic #27261 P2 #27747. At this head the PR body still reads Part of #27744, and its "Acceptance notes — Part of, not Fixes" paragraph and its Tests section (measured at 07def8cc) predate the move: they say the driver-odata wiring is left to a carrier while the diff now carries it. The face reads the committed file list and the two declaration lines, not the body's prose; the declaration lines are unchanged and right, PR-body guards is success on the current body, and a body edit moves no byte of the reviewed tree, so this record stands across it. Owning seat, before landing: edit the body to Fixes #27744, replace the stale acceptance note, and name odata-driver.conformance-kit.test.ts in Tests. Not a must-fix.
  2. File surface (claim 6510468826 as amended by 6512765336): packages/objectql/src/**, .changeset/**, plus packages/drivers/driver-odata/src/** tests only. Held: the fifth path is one *.test.ts there; the serial constraint "do not edit its package" was lifted by the amendment once feat(driver-odata): a declarative OData v4 datasource driver, published as a plugin driver id behind the engine floor #27796 landed. Answered.
  3. Merge carry-over (beacfa67): nothing of the PR's own moved; main's moves in the kit's closure are listed above and leave the floor's contract files and the spec tables untouched. Joint breakage is CI's question on the merged head: TypeScript Type Check, Test Core (8 shards) and Build Core are success there. Answered.
  4. Card task item 2, first half ("driver-odata runs it in this repository as the first consumer") — now in this diff (① item 11); second half (guide pointer) placed on docs: the external-systems guide gains the OData datasource section and the three-way choice (declarative OData, plugin driver, DataSync mirror) — epic #27261 P2 #27747 by the ruling. Items 2–6 of record 6511619545's ③ (premise correction, declared non-coverage, no new package, out-of-scope findings) stand as judged.
  5. deviations: none declared. PR author objectstack-fleet[bot] (relay write) — no non-relay login to name. Changed lines 1488, under the 3000 threshold (Check PR Size success, size/xl). No governed path.
  6. Check-runs on the head (48, every one completed, zero failure, zero in_progress): gate-carrying contexts success — Lint & Repo Gates and its lint lanes, TypeScript Type Check and its type-check lanes, Test Core and all 8 shards, Plan Test Core shards, Build Core, Governed Surface Queue Guard, Test UI, Live E2E UI, Dogfood Regression Gate, Check Changeset, Check PR Size, PR-body guards, Flag docs affected by code changes, Auto Label, filter; skipped by paths filter — Lint UI, Type Check UI, Build & E2E UI, Build UI Deps, UI Published Artifacts, the Test UI and Dogfood Regression Gate shards, Dogfood Verify CLI, Temporal Conformance (live PG + MySQL), Build Docs, the schedule gate, and the two opt-in harnesses. The shard job logs sit behind a storage host this seat's egress refuses, so the shards' conclusions are read as the verdict, which is the brief's rule; packages/drivers/* is a workspace member and the shard plan selects the packages a diff reaches.
  7. Docs-drift (6511094573, re-computed for the merge of this head): advisory; names @objectstack/objectql only and no hand-written page this diff falsifies; the guide pointer is docs: the external-systems guide gains the OData datasource section and the three-way choice (declarative OData, plugin driver, DataSync mirror) — epic #27261 P2 #27747's.
  8. Escalation closed: the previous record's item 9 (the brief named card driver-odata: a declarative OData v4 datasource driver (plugin-published id, pushdown for $filter/$orderby/$top/$skip/$count, the engine floor for the rest) — epic #27261 P2 #27741) — this brief names objectql: publish a driver conformance kit that out-of-repo plugin drivers run in their own CI, so a contract change here is caught there — epic #27261 P2 #27744.

Must-fix: none.

Implemented-by: claude/project-thread-yyuer7
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
ACCEPT · PR #27798 @ eb7990be468b9b2c888d52098d7a07c6c766eb05 · 2026-10-30T03:23Z

维护者速读

  • 改了什么: @objectstack/objectql 新导出一套驱动一致性测试包(definePluginDriverConformanceSuite 等)。仓库外的插件驱动(比如以后的 Salesforce、SAP v2 驱动)在自己的 CI 里跑它,就能验证自己满足 objectql: virtual-object correctness floor — applyDriverQueryInMemory helper, per-datasource row cap with a loud refusal, supports honoured for plugin drivers, refusal-envelope mapping for driver failures (epic #27261 P1) #27277 的兜底契约。仓库里的 OData 驱动已经接上,跑了 276 个用例。
  • 为什么改: 您 22:01Z 定的方案:厂商专用驱动不进本仓库,靠一套公开的一致性测试来保证质量。这里的契约一改,仓库外的驱动在自己的 CI 里就会发现。
  • 风险与回滚: 只新增导出和测试,运行时行为不变。changeset minor · Clause-②: yes(新增公开接口)。还原即回滚。
  • 先看: packages/objectql/src 下一致性测试包的入口,以及 packages/drivers/driver-odata/src/odata-driver.conformance-kit.test.ts。
  • 两分钟验证: pnpm --filter @objectstack/driver-odata exec vitest run src/odata-driver.conformance-kit.test.ts。
  • 需要你做的: 无,席位落地。

席位意见:ACCEPT。契约复核 PASS 在案(分诊 6513256862,CONTRACT_REVIEW_TIER,就是当前 head;此前 6511619545 在 07def8cc 也是 PASS),无 must-fix。

席位核对

  1. Shape: +1488/−0, 5 files. A new exported conformance kit in @objectstack/objectql, with its fixtures and tests. One driver-odata test runs it over the in-process OData service under both collations, with server paging at 5 rows, write probes and an outage.
  2. Premise measured: on main, no exported kit let an out-of-repo plugin driver assert the floor contract; the floor's tests were package-internal. The dev showed a deliberately wrong probe fails the kit, so the kit really exercises the driver.
  3. Gate check-runs on eb7990be: 48 runs: 35 success, 13 skipped, 0 failed or pending. mergeable_state: clean. Local: driver-odata 715 tests, objectql 9582 tests, typecheck 0, 60 derived gates exit 0.
  4. Accepted deviations: the PR body says "Part of objectql: publish a driver conformance kit that out-of-repo plugin drivers run in their own CI, so a contract change here is caught there — epic #27261 P2 #27744". The relay refuses the body patch, so per ruling 6511123974 this PR closes the card and the seat closes it at landing.
  5. Out of scope, routed: the guide pointer to the kit goes to skills + docs: teach external.connector (D7) beside credentialsRef in the datasources skill rule and the two data-modeling guides — epic #27261 P2 #27786 (scope addition 6513665711). Skills-docs: skills + docs: teach external.connector (D7) beside credentialsRef in the datasources skill rule and the two data-modeling guides — epic #27261 P2 #27786.
  6. Landing: not governed (check-governed-merges --pr 27798: 1488 lines ≤ 3000). A same-shape PASS stands on the current head, so needs:contract-review comes off. Landed by the seat through the relay.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 30, 2026 03:24
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 30, 2026 03:24
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Correction to ACCEPT 6514176452 · 2026-10-30T03:25Z

The 速读 line 风险与回滚 says changeset minor. It is '@objectstack/objectql': patch, and Check Changeset and both contract records passed on that. Nothing else changes.

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/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants