Skip to content

[Decision] Ontology over external systems, Tier B: which declarative dialect — OpenAPI as designed, OData first (the dialect Salesforce Connect and Dataverse ship), or chosen per pull by the source's protocol (epic #27261, amends D2's premise) #27299

Closed

Description

@objectstack-fleet

director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8

Ruled: 6486145005 · letter B · 2026-10-29T02:26Z

Filing gate ② (a decision only the maintainer can make), filed on the maintainer's instruction in this session, 2026-10-28, verbatim: 「你说的问题立决策卡,挂在专题下面」. It follows the re-assessment of #27261 against the connector survey behind #26702.

Placement. Epic: #27261 (parent; pm:epic subtree). Filed by session_01J8VVAmeQhJepVXSad145da. ⛔ Not a claim. No domain:* label: triage sets it, and domain:spec is expected because the answer decides whether D2's keys exist.

维护者速读

  • 一句话问题: 声明式档上线后,客户用无代码方式绑定一个 ERP 或 HR 系统的列表。如果平台没法把筛选和排序交给对方系统去做,超过 1000 行的列表就会被拒收。[Decision] Ontology over external systems: virtual objects on ADR-0015 federation — two driver tiers (plugin code · declarative OpenAPI), pushdown/identity/catalog/cache rules, Studio and AI drafts (2026 ontology research + deep design) #27261 把声明式档定成 OpenAPI。但 OpenAPI 只描述"有哪些参数",不描述"怎么筛选"。主流平台在这一档用的是 OData:Salesforce Connect 和 Dataverse 都内置 OData 适配器,都没有 OpenAPI 适配器。
  • 谁撞上(实测): 今天没有人。P2 要等第二个客户(D1)。这张卡是"P2 立卡前先定方言"。
  • 选项:
    • A 维持 OpenAPI(D2 的两个键照加)。
    • B OData 优先:筛选、排序、分页能完整下推,$metadata 直接生成对象草稿,不需要 D2 的两个新键。OpenAPI 只在拉动方系统不支持 OData 时再做。
    • C 到拉动时按对方协议二选一。
  • 推荐 B。 置信缺口:国内 ERP/HR(金蝶、用友等)是否支持 OData 没有核实。
  • 工作量: A 约 L + S,还要补 connector-openapi 已实测的四个解析缺口。B 约 L,没有新对象键。C 是 A 和 B 的上界。时机都随 P2。
  • 请回: A / B / C

Background

  • [Decision] Ontology over external systems: virtual objects on ADR-0015 federation — two driver tiers (plugin code · declarative OpenAPI), pushdown/identity/catalog/cache rules, Studio and AI drafts (2026 ontology research + deep design) #27261 Tier B (ruling 6471382887, D1/D2) is one platform driver, openapi. An object binds through object.external.operations { list, get, create?, update?, delete? } (operationIds) plus object.external.idField. Pushdown is "declared per object: filterable / sortable / pageable fields". Everything else is applied in memory under a 1,000-row cap that refuses loudly. D2 placed the two keys; D1 gated all of Tier B on a second customer pull.

  • What mainstream operational platforms ship for the same capability:

    • Salesforce Connect external data sources take the adapter values OData (2.0), OData4, SfdcOrg (cross-org), a custom Apex adapter (Apex Connector Framework), AmazonAthena and AmazonDynamoDB.
    • Dataverse virtual tables ship an OData v4 provider installed by default, custom provider plug-ins, and the virtual connector provider.
    • Neither ships an OpenAPI adapter. The end state is "OData declaratively, custom code for the rest", which maps onto Tier B (OData) plus Tier A (plugin driver).
  • Why the dialect decides pushdown.

    • OData standardises the query ($filter, $orderby, $top, $skip, $count, $select) and publishes the model ($metadata: entity sets, keys, types, navigation properties). A driver can map DriverQuery onto it generically, so supports claims nearly everything and the row cap rarely bites.
    • OpenAPI describes parameters, not their semantics. Mainstream SaaS list endpoints use bespoke query forms: SOQL, JQL, HubSpot's POST search body, Stripe's created[gte] with cursor paging. Pushdown therefore has to be authored by hand per object and per operator, or it falls to memory and the cap.
    • Measured on microsoft/PowerPlatformConnectors at 2bb74ad3: of 1,170 parsable Swagger definitions, 18 carry x-ms-pageable. Even vendor-annotated specs rarely declare paging semantics.
  • OData fits the existing binding.

    • The entity set is object.external.remoteName.
    • The key comes from $metadata, so idField is not needed.
    • The operations are implied by the protocol, so operations is not needed.
    • $metadata feeds generateObjectDraft for D6's import flow.
  • Measured gaps in the connector-openapi parsing that P2 plans to reuse:

    • Swagger 2.0 is refused as a fatal config fault, so the document must be converted offline.
    • Parameters given as $ref are skipped; the caller must pre-dereference.
    • Request bodies are JSON only.
    • YAML is not accepted.
    • The ADR-0023 CLI follow-up is unbuilt.

    Under A these are P2 work, and they are shared with connectors.

Governing text

Protocol statement.

  • A: Metadata-protocol: added, two optional object keys (as D2) plus the openapi driver config contract.
  • B: no new object key. The odata driver's own config contract is added (service root URL, version, credential per the sibling credential card).
  • C: either, decided at the pull.

Premises (read at origin/main e9c88f7c)

  1. Swagger 2.0 refused. Re-check: git grep -n "a Swagger 2.0 document" origin/main -- packages/connectors/connector-openapi/src/openapi-provider.ts.
  2. $ref parameters skipped. Re-check: git grep -n "'\$ref' in p" origin/main -- packages/connectors/connector-openapi/src/openapi-connector.ts.
  3. JSON-only bodies. Re-check: git grep -n "JSON.stringify(input.body)" origin/main -- packages/connectors/connector-openapi/src/openapi-connector.ts.
  4. No YAML. Re-check: git grep -n -i yaml origin/main -- packages/connectors/connector-openapi/src answers nothing.
  5. The x-ms-pageable count: a census of microsoft/PowerPlatformConnectors at 2bb74ad3 (1,197 folders, 1,166 with parsable JSON, Swagger 2.0 throughout). It is outside this repository; the method was a JSON walk counting connectors whose definition carries the extension.

Options

Option What it does What the customer sees Effort
A OpenAPI (as designed) driver-openapi; D2's two keys; pushdown declared per object field; parsing upgrades in connector-openapi (2.0 conversion, $ref, YAML, non-JSON bodies) Any spec'd REST API is bindable. Large lists work only where the author declared the filters. spec S + server L + parsing M (not measured) · precedent: connector-openapi (ADR-0023)
B OData first driver-odata (v4; v2 when a pull needs it); $metadata import; no new object keys; OpenAPI only on a pull from a non-OData source SAP S/4HANA, SuccessFactors and Dynamics-class sources bind with full filtering and paging. Spec-only REST APIs wait for Tier A code or a later OpenAPI pull. server L + ui M (import) · no precedent
C per pull At the second pull, read the source's protocol and file A's or B's children The same as whichever is chosen the upper bound of A and B

In business terms. A: a universal plug that fits every socket loosely, so heavy loads slip. B: the industry-standard plug, which fits standard sockets exactly and leaves the rest for now. C: buy the plug when you see the socket.

四维分析(业务立场)

  • 项目长远合理性(领起)。 两年后的样子:外部业务系统的数据以对象的形式被无代码绑定,筛选、排序、分页由源系统执行,大列表照常可用;标准协议的源零代码,非标准的源写一个代码档 driver。Salesforce Connect(OData 2.0/4.0 + Apex 自定义适配器)和 Dataverse(OData v4 provider + 自定义 provider)都是这个终态。B 直接到达,而且用现有的 remoteName 就能表达绑定。A 把声明式档建在一个不描述查询语义的格式上,下推能力只能靠作者逐对象逐字段手写,规模一上来就撞行上限。
  • 实际业务拉动。 今天零拉动,Tier B 本来就等第二个客户(D1)。D1 说的目标是 ERP / HR / MES 的无代码接入,这一类里 SAP、SuccessFactors、Dynamics 原生支持 OData;国内系统是否支持未核实,这是本推荐最大的盲区。所以只定方言优先级,实施随拉动。
  • 防 AI 犯错。 B 的查询语义由协议定义,AI 只写实体集名,没有可以写错的下推声明;$metadata 漂移在启动时可检测。A 让 AI 为每个对象手写"哪些字段可筛选、映射到哪个参数",写错时要么静默不下推(列表变慢,最终撞上限被拒),要么映射到错误参数而返回错误的行。后者在引擎兜底之外,最危险。
  • 创业阶段不扩散。 B 不加对象键,D2 的两个键不再需要;A 加两个键,外加一份解析升级。C 推迟决定,但 P2 立卡时还要再裁一次。

os-decision-facets

  • ① 项目长远合理性:B 是 Salesforce Connect 与 Dataverse 的同形终态(OData 声明式 + 自定义代码),用现有 remoteName 表达;A 建在不描述查询语义的格式上,下推靠手写。
  • ② 实际业务拉动:今天零拉动(D1 等第二个客户);目标类 ERP/HR 里的 SAP / SuccessFactors / Dynamics 原生 OData,国内系统未核实。
  • ③ 防 AI 犯错:B 的查询语义由协议给定、无手写下推声明;A 的手写映射写错会返回错误的行,在引擎兜底之外。
  • ④ 创业阶段不扩散:B 零新对象键;A 两个键加解析升级;C 推迟但要再裁一次。

Prior rulings read: "OData datasource driver external objects" → 9 hits, none on a declarative dialect (#27279 open, P1 Studio marking; the rest closed ADR-0015 door fixes); ruling 6471382887 D1 / D2; ADR-0015 §3.1; ADR-0023 status; thread: none.

Recommendation

  • B. A is not taken: it fixes the format before knowing whether its queries can be pushed down. C is not taken: it defers the same question to the pull and still needs a ruling then.
  • 只看①选 B;②③④ 是否翻转:否(② 只决定时机:仍随第二个客户)。
  • Confidence gap:
    • The second customer's source protocol is unknown. If it is a domestic ERP / HR / MES without OData, B's first deliverable does not serve it, and either a Tier A driver or an OpenAPI pull is needed.
    • OData v2 versus v4 coverage was not measured.
    • The Salesforce Connect and Dataverse readings come from search summaries of their public docs, not from pages fetched in this session.

After the ruling

Related

#27261 (epic) · ruling 6471382887 · #27130 · ADR-0015 · ADR-0023 · #26702 (the connector survey) · and the sibling decision card on datasource credentials, filed alongside this one.


Generated by Claude Code

Activity

  1. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    on Oct 28, 2026
  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8

    Brief-claimed-by: session_01Utp6CbQLcy8yg6vhJVXXi8 · batch #426 · 2026-10-29T02:15Z

    Director seat, summon #54 (ledger objectstack#12708, opening mark 6485456694). Presented to the maintainer in batch #426 as item 2 of 3, together with #27298 and #27302.

    Seat increment (re-read at origin/main 73725c20, 2026-10-29T02:15Z):

    • Premises re-run, all hold: Swagger 2.0 refused as a fatal config fault (openapi-provider.ts L48 / L56); $ref parameters skipped (openapi-connector.ts L295); JSON-only bodies (L195); yaml 0 hits under connector-openapi/src; remoteName is the object binding's table name (object.zod.ts L1344–1346); ADR-0023 status still "CLI + examples pending". Added: odata appears in 23 files under packages/spec/src already (the query-transport vocabulary), so the word is not new to the contract.
    • Prior rulings: check-prior-rulings.mjs --card 27299 names ADR-0076 D9, ADR-0023 §4, ADR-0080 §3, ADR-0027 §12, ADR-0059 §3, ADR-0062 D5 — term matches, none chooses a declarative dialect for external datasources. Thread: [Decision] Ontology over external systems: virtual objects on ADR-0015 federation — two driver tiers (plugin code · declarative OpenAPI), pushdown/identity/catalog/cache rules, Studio and AI drafts (2026 ontology research + deep design) #27261 ruling 6471382887 D1 (Tier B filed on the second pull) and D2 (object.external.operations + idField), whose premise — Tier B is OpenAPI — is what this card asks to amend. The epic body §4 and its P2 row still say OpenAPI and driver-openapi.
    • Facets: agree with the card on all four, with one sharpening of ②. ① the mainstream shape for "bind a standard business system declaratively" is OData (Salesforce Connect's OData 2.0 / 4.0 adapters plus Apex custom adapters; Dataverse's default OData v4 virtual-table provider plus custom providers), and the existing remoteName binding already expresses it; OpenAPI describes parameters, not query semantics, so pushdown would be hand-authored per object and the 1,000-row cap would bite on every large list. ② the pull is still zero (D1); the card's own blind spot is the one that matters for timing: the first no-code customer may bring a domestic ERP / HR system (金蝶、用友、北森 and the like) whose OData support is unverified — under B that customer waits for a Tier A driver or an OpenAPI pull. ③ B leaves the author nothing to get wrong in pushdown; A's hand-written filter maps can return wrong rows outside the engine's fallback. ④ B files no new object key; D2's two keys are then not filed.
    • Seat's letter: B — OData first (v4; v2 when a pull needs it), $metadata import, no new object keys; OpenAPI only on a pull from a non-OData source. Not A (fixes a format before knowing whether its queries push down), not C (the same ruling again at the pull, with P2's children unfiled until then). Nothing is dispatched now.
    • Workload of B: domain:server driver-odata L + domain:ui $metadata import M + domain:devx guide S · at P2 · no precedent (A would be spec S + server L + parsing M on top of connector-openapi). Confidence gaps kept from the card: the second customer's protocol; OData v2 versus v4 coverage; the Salesforce / Dataverse readings come from public documentation summaries, not pages fetched in this session.
    • After the ruling: the epic PM amends [Decision] Ontology over external systems: virtual objects on ADR-0015 federation — two driver tiers (plugin code · declarative OpenAPI), pushdown/identity/catalog/cache rules, Studio and AI drafts (2026 ontology research + deep design) #27261's body (Tier B reads "declarative OData; OpenAPI on a non-OData pull"; D2's keys not filed) and P2's children are renamed accordingly; this card then leaves the box under pm:epic.

    Prior rulings read: OData, OpenAPI, declarative, datasource, pushdown, remoteName → ADR-0015 §3.1, ADR-0023; thread: 6471382887 D1 / D2 (#27261); repo: objectstack-ai/objectstack

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8

    Ruling: batch #426 item 2 · letter B · maintainer 「同意」 2026-10-29T02:23Z

    Director seat, summon #54 (ledger objectstack#12708, opening mark 6485456694, brief 6486027050). The maintainer answered batch #426 in the summon chat with one word, verbatim: 「同意」 — the seat's letter on each of the three cards; for this card B.

    What the ruling binds (letter B — OData first): #27261's Tier B is a declarative OData datasource driver (v4; v2 when a pull needs it), not OpenAPI. The object binding is the existing object.external.remoteName (the entity set); the key comes from $metadata, the operations are implied by the protocol, so D2's two keys (object.external.operations, object.external.idField) are not filed. $metadata feeds generateObjectDraft for D6's import. Filtering, sorting, paging and $count push down generically; whatever supports does not claim stays under the engine's in-memory fallback and row cap (standing rule of 6471382887). OpenAPI returns only on a pull from a source that has no OData endpoint, as its own card; an OAuth-protected or non-OData source meanwhile takes a Tier A plugin driver. D1 stands: Tier B's children are filed on the second customer pull, not now. Not taken: A (fixes the format before its queries are known to push down; hand-written filter maps can return wrong rows), C (the same ruling again at the pull).

    Governing text: ruling 6471382887 D1 / D2 (D2's premise amended here); ADR-0015 §3.1 (one Object shape, external is a property of the binding); ADR-0023 (connector-openapi stays the connector-side parser, untouched by this ruling). Premises re-read at origin/main 73725c20 (brief 6486027050).

    Workload of the letter: domain:server driver-odata L + domain:ui $metadata import M + domain:devx guide S · filed with P2 on the second customer pull · no precedent. Recorded blind spot for P2's filing: the second customer's protocol (a domestic ERP / HR / MES without OData takes a Tier A driver or an OpenAPI pull); OData v2 versus v4 coverage unmeasured.

    Execution: nothing is dispatched. The epic PM of #27261 amends the epic body — Tier B reads "declarative OData; OpenAPI on a non-OData pull", the P2 row's children become driver-odata, $metadata import and the guide section, D2's keys struck — and this card closes completed in this act (the ruling is its content; P2's children cite this comment on a Ruling-ref: line when filed).

    os-decision-facets
    ① 长远:Salesforce Connect 与 Dataverse 的同形终态(OData 声明式 + 自定义代码档),现有 remoteName 即可表达
    ② 拉动:今天零(D1 等第二个客户);国内 ERP/HR 的 OData 支持未核实,是记录在案的盲区
    ③ 防错:查询语义由协议给定,AI 无可写错的下推声明;A 的手写映射会返回错误的行
    ④ 不扩散:零新对象键,D2 的两个键不再立

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8

    Closed completed · 2026-10-29T02:30Z · ruling 6486145005 (batch #426 item 2, letter B: Tier B is a declarative OData driver, OpenAPI only on a non-OData pull, D2's two keys not filed). The epic PM amends #27261's Tier B wording and P2 row (pointer left there); P2's children cite the ruling when filed. needs-user-decision leaves with the close; pm:epic stays.

  5. removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions