Skip to content

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

Closed

Description

@objectstack-fleet

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ③ maintainer-directed — 田騏 at 2026-10-29T21:58Z 「现在启动全部 P2」 (epic comment 6509296853, D1 amended), at 2026-10-29T21:59Z 「OData 驱动 只做 v4」 (6509311454), at 2026-10-29T22:01Z 「同意」 (generic P2 parts open source in this repository; vendor-specific drivers such as Salesforce stay out of it).
Ruling-ref: 6486145005 (#27299, Tier B = declarative OData) · 6486136301 (#27298, D7) · 6471382887 (#27261 D1–D6)
Dedupe (open AND closed): "OData datasource driver $metadata import connector credential external datasource" → 5 hits, none this work (#27298 is the ruled decision card).
Reader: the epic PM (#27261) dispatches it in the epic subtree.
type: Feature

维护者速读

  • 做什么: 新包 packages/drivers/driver-odata,一个声明式 OData v4 数据源驱动。管理员声明服务地址和实体集(object.external.remoteName)即可把 SAP、Dynamics 365 等系统的实体作为对象实时读写,不写代码。
  • 为什么: 面向全球客户,OData 是 SAP / Dynamics 365 的标准接口,也是 Salesforce Connect、Dataverse 声明式外部数据的底座。
  • 范围: 只做 v4;代码里留「方言」接缝,将来 v2 另立卡补上。
  • 需要您做的: 无。

Task

Clause-②: decided by the dev (a new package; no spec key is planned) · Metadata-protocol: none unless the config contract lands in spec.

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Claim: PM loop round 8 2026-10-29T22:10Z
    Session: session_017ihL2c5z3q4h8inrn5kwqV (epic PM); implementing card thread session_017ihL2gRrVctbMWaHbABDch
    Account: marchtian
    Branch: claude/project-thread-wj01ct (the card thread's platform-assigned branch — measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-27741
    Domain: domain:server
    Seat: epic:#27261#1 (epic PM; no domain:* seat). Maintainer order, 田騏 2026-10-29T21:58Z: 「现在启动全部 P2」; 2026-10-29T21:59Z 「OData 驱动 只做 v4」
    File surface: packages/drivers/driver-odata/** (new package), the server plugin that publishes driver.odata, workspace wiring the new package needs (pnpm-workspace.yaml / root configs / lockfile), .changeset/** (stop on breach, including any packages/spec/src/** or DRIVER_VOCABULARY edit; explain in the report)
    Container & model: L, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: epic #27261 P2 (ruling 6486145005, letter B); the engine floor from #27277 is the correctness net it relies on
    Thread-read: none
    Premise: holds — on origin/main no OData driver exists (packages/drivers: memory, mongodb, sql, sqlite-wasm, turso), and needsExternalDriverFloor floors any non-built-in driver id on a non-managed datasource.
    Serial constraints: #27745 and #27747 are Blocked-by this card. #27742 / #27746 (D7) are not; use credentialsRef auth only here.

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2gRrVctbMWaHbABDch
    os-dev-report

    {
      "issue": 27741,
      "status": "done",
      "branch": "claude/project-thread-wj01ct",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27796",
      "session": "session_017ihL2gRrVctbMWaHbABDch",
      "premise_still_valid": true,
      "summary": "Premise held on origin/main 81d00a4e: packages/drivers had memory, mongodb, sql, sqlite-wasm, turso and no OData driver; needsExternalDriverFloor floors any non-built-in driver id on a non-managed datasource. New package @objectstack/driver-odata (fixed group, 18.2.0): createODataDriverPlugin publishes ODataDriver as driver.NAME (default driver.odata), a plugin driver id; DRIVER_VOCABULARY and packages/spec/src untouched. Config contract (strict zod, parsed at plugin build): name, serviceRoot (http/https, refused with userinfo or query), version 4.0, auth none|bearer|basic(username), pageSize, requestTimeoutMs; no credential key. Credential = the datasource's external.credentialsRef, read from engine.listDatasourceDefs() and resolved through options.secrets (the host's SecretBinder read half) at the first request, re-resolved once on 401; fails closed on missing ref, missing store, unresolvable handle, undeclared datasource, ref under scheme none. Reads: $filter/$orderby/$select/$top/$skip, @odata.nextLink (same origin only), key appended to $orderby on paged reads, count via $count=true only when the where translated completely. Translator never narrower: $not never pushed, $ne/$nin on nullable carry 'or P eq null', text only eq/in/contains/startswith/endswith/ASCII icontains, unspellable comparands left to the floor. ReadableIds leaves refused by the driver itself (readableIdsDriverRefusal). Writes POST/PATCH/DELETE by key with If-Match from the last read's @odata.etag, declared properties only; upsert/bulk/transactions/execute/dropTable refused by name (400). introspectSchema: entity sets, EDM types, keys, single-valued navigations with bound set as lookup candidates (for #27745). Dialect seam: ODataDialect (headers, envelope, etag, error body, CSDL reader, literals, functions, in-list, count option); only odataV4Dialect ships. Two files outside the claimed surface, both mechanical consequences of a new published package: packages/spec/llms.txt package count 72 to 73 (check:llms-txt red otherwise; spec patch added to the changeset) — not packages/spec/src. D7 (#27746) case: not on main at bcc0a1a3 (git grep of origin/main packages/types/src for a connector-credential seam: 0 hits), so not wired; the auth header comes from one function, ODataTransport.authorization() in transport.ts, fed by the plugin's credential() source in plugin.ts — the later wiring replaces those two.",
      "tests": "On head bcc0a1a3: pnpm --filter @objectstack/driver-odata test = Test Files 6 passed (6), Tests 425 passed (425), exit 0; typecheck exit 0; eslint packages/drivers/driver-odata exit 0. Suites: filter.test.ts (54-row translation table), dialect-v4.test.ts (CSDL reader + envelope), odata-driver.test.ts (recorded TripPin-shaped service: reads, paging, writes, ETag, keys, introspection, credentials), odata-driver.floor.test.ts (real ObjectQL engine + floor, objects bound by engine.syncSchemas: in-memory application of $like, count through floor, 400 read = INVALID_QUERY, 400 write = EXTERNAL_WRITE_REFUSED 422, 503 withheld), plugin.test.ts (service name, config refusals, bearer/basic, 5 fail-closed), odata-driver.conformance.test.ts (all 11 shared case-sets, 326 cases, through engine+floor over an in-process OData service that evaluates the $filter, read sets under ordinal and case-insensitive collation). Ablation (one-shot): nullable $ne disjunction removed in filter.ts = 1 failed / 53 passed (the '$ne on a nullable property' row); restored, blob equals HEAD, git diff HEAD empty; tests import source.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands (no paths) on merged head bcc0a1a3 derived 77; --ran: '77 derived famil(ies) accounted for — 77 run, 0 NOT-MEASURED (a DERIVED zero — all 77 recorded an exit code and none of them is 3)'. All 77 exit 0. First pass on 02401a71 (72 derived): check:llms-txt exit 1 (count 72 vs 73) and check:driver-conformance exit 1 (11 CONSUMED cells) — both fixed, now 66 covered cells, 0 DEBT, 0 EXEMPT; check-plugin-teardown-shape --self-test and check:dual-build-cjs-loads exit 3 (prerequisites) — fixture commit fetched, dist present on the rerun, both exit 0. CI check-runs on #27796 not yet read (PR just opened).",
      "dogfood_battery": "n/a — no ! in the title and no (narrowing) arm",
      "line_budget": "no line budget in the claim; three-dot diff vs origin/main: 26 files, +3689/-2 = 3691 changed lines, OVER the 3000 human-merge threshold — lands only with an authorized APPROVE (LICENSE copy is 201 lines)",
      "files_changed": [
        "packages/drivers/driver-odata/** (new package: package.json, tsconfig.json, vitest.config.ts, README.md, LICENSE, src/{index,config,dialect,dialect-v4,filter,transport,odata-driver,plugin}.ts, 6 test files, src/__fixtures__/{trippin-metadata.xml,recorded-service.ts,odata-service.ts})",
        ".changeset/27741-driver-odata.md",
        ".changeset/config.json",
        "pnpm-lock.yaml",
        "packages/spec/llms.txt"
      ],
      "mcp_calls": "reads only: issue_read (get #27741, get_comments #27741, get_comments #27666), pull_request_read (get #27648 for the body form). No MCP writes.",
      "api_writes": "1 relay write so far as objectstack-fleet[bot]: fleet-write pr_create, run 40974161877 = #27796 (draft forced, labels [enhancement], no assignees; read-back 12930 bytes sent, 12930 stored, identical). Plus this report via post-stamped --comment=27741. Plus git pushes of the branch (head bcc0a1a3).",
      "open_questions": [
        "Landing needs 田騏's APPROVE on #27796 (3691 lines over the 3000 threshold)."
      ],
      "deviations": [
        "Report comment: the seat signature line comes first and os-dev-report second, as post-stamped requires.",
        "packages/spec/llms.txt edited (two numbers) outside the claimed file surface; it is not packages/spec/src and not DRIVER_VOCABULARY; the edit is forced by check:llms-txt on any new published package. @objectstack/spec added to the changeset for it.",
        "A first gate pass was interrupted by a container restart and resumed from the recorded exit codes; the final --ran reconciliation is over all 77 on bcc0a1a3."
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — no $batch arm: the bulk verbs are refused rather than looped; a follow-up if a customer needs atomic multi-row writes. Dedupe words: odata $batch bulk write driver-odata"
      ]
    }
  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Landed · 2026-10-30T01:33Z

    PR #27796 merged through the queue as 0a4b0fb223 on 2026-10-30T01:32:02Z (head 7f91132b; seat ACCEPT 6512051940, the review of record for a no-contract-face PR; APPROVED by os-zhuang past the 3000-line gate). @objectstack/driver-odata is on main: a declarative OData v4 driver published as the plugin driver id driver.odata, held behind the engine floor, with a v2 dialect seam. Follow-ons: #27798 (#27744) adds the driver-odata test that runs the conformance kit; #27819 (#27746) wires the D7 connector credential into ODataTransport.authorization()'s source; #27745 ($metadata import) and #27747 (guide) are unblocked and dispatched next.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions