Skip to content

[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

Open

Description

@objectstack-fleet

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Epic PM session: session_017ihL2c5z3q4h8inrn5kwqV (project thread, account marchtian). Phase plan agreed with the maintainer in that thread; see the registration comment.

Ruled: 6471382887 · letters D1 both-in-order · D2 object.external · D3 non-goal · D4 off · D5 off · D6 P1/P2 · 2026-10-28T14:19Z

Epic: ontology over external systems (virtual objects on ADR-0015 federation) · pm:epic · opened by the director seat (summon #53, session_01QSeKYo7qYsX6HsAneHBMP2) on ruling 6471382887 (maintainer 「立专题卡」, then 「原则同意,按步骤推进。」 2026-10-28).

Territory (declared): packages/objectql/src (driver routing, in-memory query helper, row cap, refusal mapping), packages/spec/src/contracts/data-driver.ts (supports reading only; P2 adds object.external.operations / idField under packages/spec/src/data/object.zod.ts and external-catalog.zod.ts), packages/runtime/src/external-validation-plugin.ts, ui/packages/app-shell/src/views/metadata-admin (read-only marking; P2 datasource page), content/docs (the guide). P2 adds packages/drivers/driver-openapi when pulled.

Children

P0 (now)

P1 — Tier A formal (P0 landed as PR #27330; #27279 → PR #27316; #27278 → PR #27349; #27332 → PR #27422; #27277 → PR #27403; #27425 → PR #27476; #27472 → PR #27639; #27471 → PR #27648)

P2 — Tier B declarative OpenAPI (filed on the second customer pull, D1): spec external.operations + idField + catalog source: 'openapi' (S) · driver-openapi (L) · Studio datasource page + import (M) · generateObjectDraft for OpenAPI (S) · showcase example (S).

Critical path: #27276 → #27277 / #27278 / #27279 (parallel) → #27130 unblocks on #27276 + #27278 (unblocked 2026-10-29). P1 remainder: #27425 (p1, security) first.

P3 — by pull, not filed (maintainer ruling 6487532394 on #27277, plus seat carries): verified-pushdown credential (conformance kit → the engine sends offset/limit to a trusted plugin driver); count reusing the candidate set; driver-memory converging on the engine evaluator; an engine semi-join so a controlled_by_parent external object (a ReadableIds leaf, refused today) can be read. P2 prerequisite: now card #27666 (the connection service registers datasource defs without driver, and unregisterDriver drops the stored id on reconnect).


Filing gate ③ (maintainer-directed). The maintainer, in the director summon (session_01QSeKYo7qYsX6HsAneHBMP2, 2026-10-28), after the #27130 route-d design: 「这个可以用来对接外部系统,定义本体吗?」 → 「你帮我深度设计一下,这个需求更大」 → 「你可以上网搜索调研一下目前主流的本体开发需求,以及和我们项目的匹配」. This card carries the research and the design; the maintainer rules on the decision points in §9. ⛔ Not a claim. No domain:*: triage sets (spec + server expected). Dedupe: ADR-0015 (external datasource federation, Accepted; Studio UI pending), #27130 (the first customer pull, route d), #8345 (unrelated), no open card designs API-backed federation or an OpenAPI datasource.

Ruling-ref: none yet (decision card). Prior rulings read: federation|external datasource|virtual|schemaMode|external_catalog → ADR-0015 §1–§6 (one Object shape; three gates; external_catalog; IExternalDatasourceService; non-goals: cross-datasource joins, distributed transactions, reverse DDL); ADR-0049; ruling C on #23082 (view-level api retired; the governed door wins); #27130 design 6468011868.

维护者速读

  • 一句话: 把「行住在哪」从「我们的库 / 联邦数据库」扩到「任何有 API 的系统」,本体层(对象、关系、动作、权限、流程、Agent 工具)一个字不改;主流平台 2026 年的本体需求我们已有七成,缺的正是这一块加 Studio/AI 的「从外部系统生成本体草稿」。
  • 要裁的: §9 的六个决策点(两档都做还是只代码档;声明档映射键放哪;跨源联查保持非目标;缓存默认关;写回默认关;Studio 范围)。
  • 工作量: P0 spike S(object-gantt can no longer write back to a same-origin endpoint after #23082 — a measured producer of the retired view-level api source (read + write) #27130);P1 代码档正式化约 3 个 M;P2 声明式 OpenAPI 数据源约 1 S + 2 M + 1 L;P3 增强按拉动。总计约 12–18 个 agent 天,分三次可独立交付。
  • 推荐: D1 两档都做但按序(代码档先,声明档等第二个客户);D2 映射放 object.external;D3 保持非目标;D4 关;D5 关;D6 Studio 先只读。

1. Research: mainstream ontology needs in 2026, and the match

Sources are search summaries quoting the vendors' docs; palantir.com and learn.microsoft.com are not reachable from this session's network, so their pages were read through those summaries only.

# Need (who states it) Mainstream shape ObjectStack today Gap
N1 Ontology = semantic elements (object types, properties, links) + kinetic elements (actions, functions, dynamic security) — Palantir core concepts objects / links / actions / functions NORTH-STAR: objects, fields, relationships, actions, permissions, flows, agents, tools; formulas + hooks as functions; RLS + permission sets as dynamic security none
N2 Objects backed by data that is not moved: datasets and virtual tables (Palantir), zero-copy DLO → DMO (Salesforce Data 360), virtual tables over OData or a custom provider (Dataverse), remote tables fetched at query time (ServiceNow, 1,000-row default), entities bound to OneLake (Fabric IQ) the object is a binding over a source ADR-0015: federated DATABASES (schemaMode, object.external, three gates, catalog, service); backend / REST / CLI landed, Studio pending API-backed and computed sources — this card
N3 Writeback with governance: actions bundle edits + side effects; edits stored in a writeback dataset, not the source (Palantir); providers implement Create/Update/Delete (Dataverse); editing is opt-in (ServiceNow) write gate + action semantics hooks + actions on managed objects; ADR-0015 gate 3 (double opt-in allowWrites + writable) an "edits overlay" for read-only sources (Palantir's writeback dataset) — not now
N4 Agents consume the ontology: Ontology MCP exposes objects, predefined actions and queries with restrictions; typed OSDK; Data 360 MCP server (2026-05); Fabric IQ ontology as the agents' map; "active ontology via MCP" (Atlan) MCP + typed SDK + action allow-lists packages/mcp (list_objects, describe_object, metadata_assistant), client SDK packages, ADR-0025 plugin tools action exposure and allow-lists per agent: partial (verify in a follow-up card)
N5 A semantic / metrics layer for BI and text-to-SQL (dbt, Cube, AtScale; Snowflake semantic views GA 2026-03, Databricks metric views GA 2026-04) metrics + dimensions defined once service-analytics datasets and measures fine for now; agents query via datasets
N6 Ontology drafts generated from existing models: Fabric IQ's ontology agent proposes entities, relationships and rules from semantic models, schemas and documents (Gartner: may be incomplete / siloed) generate, then curate IExternalDatasourceService.generateObjectDraft / importObject from a remote DB from an OpenAPI document — §4 P2; from documents — not now
N7 Governance at the ontology boundary: security and compliance evaluated there, not at the model; RLS on virtual tables enforced in the source (Dataverse) permissions on the object object permission sets + RLS apply to federated objects "exactly as managed" (datasource guidance) none
N8 Rules on the ontology (Fabric IQ natural-language rules; ontology-constrained agents, arXiv) validation rules spec validation rules, refusals with prescriptions natural-language rules — not our shape
N9 Start small: one domain, 20–50 concepts, competency questions; FK links, join datasets for many-to-many method our strictness posture (AI-written ontology refused loudly) matches none

Positioning read. Palantir and Fabric IQ sit over lakehouses for analysis and decisions; Dataverse, ServiceNow and ObjectStack are OPERATIONAL ontologies — the ontology is the app (views, forms, flows, actions). Our differentiators hold: the ontology is written by AI and refused loudly when wrong, is diffable in the customer's repository, exportable, self-hostable. The one mainstream capability we lack in that operational class is the Dataverse "virtual table over an API with a custom provider" and its declarative OData sibling. That is exactly #27130's pull, generalized.

2. Goal and non-goals of this design

Goal. An external system's entities become ontology objects without moving data: rows are read through a driver (plugin code now; a declarative OpenAPI driver later), every consumer (views, reports, permissions, hooks, audit, MCP, SDK) sees a normal Object (ADR-0015 §3.1), writes go through the platform's gates. Non-goals (kept from ADR-0015 §2.2 and added): cross-datasource joins pushed down (reads across sources are id-based lookups and in-memory expands), distributed transactions, reverse DDL, replacing DataSync for bulk mirrors, an edits-overlay for read-only sources, natural-language rules, a lakehouse zero-copy engine.

3. Conceptual model (five layers, two driver tiers)

Ontology layer    object · field · relationship · action · permission · flow · agent/tool   (unchanged)
Binding layer     object.datasource → datasource.schemaMode ≠ managed → object.external { remoteName, writable, columnMap, [operations] }
Datasource layer  managed DB · federated DB (ADR-0015, driver-sql …) · API / computed (THIS: plugin driver, OpenAPI driver)
Driver layer      IDataDriver + supports (pushdown capabilities) + optional introspectSchema / registerExternalObject
Integration layer connectors (rest / openapi / mcp / imap / slack) — outbound actions from flows; unchanged, complementary

Tier A — plugin driver (code): a server plugin publishes driver.<name> (plugin.ts:1145–1158); find / findOne / count / update as the system allows; the rest refuse by name. Any system, any computation. (#27130's route d.)
Tier B — declarative OpenAPI driver: one platform driver openapi that reads a datasource config { driver: 'openapi', config: { spec: ref, baseUrl, auth: credentialsRef, pageStyle, rateLimit } } and an object binding external.operations { list, get, create?, update?, delete? } naming operationIds plus a idField; the driver maps DriverQuery to query parameters it can push down (declared per object: filterable / sortable / pageable fields) and does the rest in memory. Reuses connector-openapi's document parsing (ADR-0023). No code for the standard REST case.

4. Protocol surface (spec) — kept minimal

  • Tier A: zero spec change. Everything is already in the contract: plugin-contributed driver ids (datasource.zod.ts:186), schemaMode external, external.allowWrites, external.validation.checkOnBoot (datasource.zod.ts:316), object.external (object.zod.ts:1296), IDataDriver.supports for pushdown.
  • Tier B: two additive keys (Metadata-protocol: added, minor): object.external.operations (optional; refused unless the object's datasource driver is openapi; each entry an operationId that must exist in the referenced document — checked at build) and object.external.idField (optional; default id). The openapi driver's config shape lives in the driver's own config contract (driver/config-registry.zod.ts), not in the datasource schema. external_catalog gains source: 'openapi' with the document hash as the drift signal.
  • Not added: any URL on a view or page element (ruling C stands); a parallel external_object type (ADR-0015 §3.1); a cache key on the object (caching is datasource config).

5. Runtime rules (the three ADR-0015 gates, extended to API sources)

  1. DDL gate: unchanged — forbidden on external objects.
  2. Boot validation: DB sources introspect; API sources validate the OpenAPI document hash against the catalog snapshot and, when checkOnBoot is true, perform one list probe with limit 1; a plugin driver with no introspectSchema is validated by its own contract test (the guide requires one "shape pin" test per driver).
  3. Write gate: allowWrites (datasource) AND writable (object) AND, for Tier B, the operation named (update without operations.update refuses at build). Writes are per record, one system at a time; enforceTransactionOrigin keeps cross-datasource writes where ADR-0057 put them.
  4. Pushdown and fallback: the driver's supports says what it pushes down (filters, sort, pagination, in, aggregate); the engine applies the remainder in memory through ONE shared helper (@objectstack/objectql exports applyDriverQueryInMemory), with a hard row cap per datasource (config.maxRows, default 1,000 — the ServiceNow figure) and a loud refusal above it: "narrow the filter or mirror via DataSync".
  5. Identity: idField must be stable and unique in the source; columnMap maps names only. Relationships across sources are id-based lookups; expands batch through find with an in filter when supports.inFilter, else findOne per id under the row cap.
  6. Caching: off by default; config.cache: { ttlSeconds } per datasource caches find results keyed by query for that TTL; writes invalidate. Never for managed.
  7. Errors: driver / HTTP failures map to the platform refusal envelope (EXTERNAL_SOURCE_UNAVAILABLE, EXTERNAL_WRITE_REFUSED with the source's message), never a silent empty list.
  8. Credentials: only external.credentialsRef; the egress allow-list per datasource host is a deployment setting, not metadata.
  9. Permissions and audit: object permission sets and RLS apply unchanged; every external write is audited with the datasource name.

6. Studio and AI

  • Studio (ADR-0015's pending half): a Datasources page listing managed / federated / API sources with health and catalog drift; an external object's fields shown read-only with their source; for Tier B an "import object from OpenAPI" flow that calls generateObjectDraft on the document's schema components (N6) and lets the author pick operations.
  • AI: the same generateObjectDraft is an MCP tool (list_remote_tables / import_object are already in the service contract); the guide gives AI authors the three-way rule — computed rows, data that must not land, or real-time ⇒ virtual object; slow-changing reference data ⇒ real object + DataSync flow.

7. Phases and workload

Phase Content Lane · size Depends on
P0 #27130 spike: plugin driver + external object boots, reads with filters, formula over current_user, hook → driver.update, gantt drag server · S —
P1 — Tier A formal applyDriverQueryInMemory helper + row cap; supports honoured by the engine for plugin drivers; refusal envelope mapping; driver "shape pin" test template; guide "a virtual object: rows from your server" with the five business examples; Studio read-only field marking objectql M · docs S · ui S P0
P2 — Tier B declarative spec: external.operations + idField + catalog source: 'openapi' (S); driver-openapi package: document parsing reused from connector-openapi, list / get / update / create / delete mapping, pushdown declaration, paging styles, auth via credentialsRef (L); Studio import-from-OpenAPI + datasource page (M); generateObjectDraft for OpenAPI schemas (S); example: showcase binds a public OpenAPI sample as an object (S) spec S · server L · ui M · devx S P1
P3 — enhancements by pull cache TTL; aggregate pushdown; DataSync mirror recipe; edits overlay (if a customer needs edits on a read-only source) per card P2

Totals: P0 ≈ 0.5 agent-day; P1 ≈ 3–4; P2 ≈ 8–10; P3 by pull. About 12–15 agent-days through P2; calendar 4–6 weeks at two lanes. Each phase ships on its own: P1 serves the #27130 class of customer; P2 serves the no-code ERP / HR / MES integrations.

8. Risks

  • Silent wrong rows when a driver ignores filters → the in-memory helper is applied by the ENGINE on anything the driver's supports does not claim; a driver cannot opt out of correctness, only of performance.
  • Unbounded reads → the row cap refuses loudly; the guide names DataSync for mirrors.
  • Source shape drift → catalog hash + boot probe (Tier B); shape pin test (Tier A).
  • Credential leakage into metadata → only credentialsRef; the build refuses inline secrets (existing rule).
  • Two doors for writes → none: the view-level api stays retired; all writes are driver.update under the gates.
  • Over-building Tier B before a second customer → P2 is gated on pull (D1).

9. Decision points for the maintainer (defaults bold)

  • D1 Tiers: both, in order — Tier A now (P0–P1), Tier B when a second customer asks for a no-code REST source / Tier A only / both now.
  • D2 Tier B mapping keys live on object.external.operations + idField / on the datasource config.
  • D3 Cross-datasource joins pushed down: stay a non-goal (ADR-0015 §2.2); id-based lookups and in-memory expands only.
  • D4 Caching: off by default, opt-in TTL per datasource.
  • D5 Writes: off by default (ADR-0015 double opt-in stands), per-operation for Tier B.
  • D6 Studio: read-only field marking in P1; datasource page + OpenAPI import in P2 / all in P1.

os-decision-facets

After the ruling

D1 both-in-order ⇒ #27130 proceeds as ruled (P0 spike card first); this card becomes the P1/P2 epic parent (pm:epic) with P1 children filed after the spike passes and P2 children filed on the second pull; the guide card lands with P1. Tier-A-only ⇒ the same without P2. Both-now ⇒ P2 children filed at once, spec first.

Activity

  1. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Triage: lane domain:spec · p2 kept · decision stays with the maintainer · 2026-10-28T14:02Z

    ⛔ Not a claim.

    • Read fresh. A maintainer-directed decision card (research plus design): virtual objects over API-backed systems, built on ADR-0015 federation, in two driver tiers, with the six decision points in §9.
    • Lane domain:spec: the deciding output is an ADR-0015 amendment and the object.external mapping (D2). The server work follows from it. One domain per card, so the server half is carried by the implementation cards once the maintainer rules.
    • p2, as filed. It is a decision card, and nothing is added until the maintainer rules. Under 「协议新增定 p1」, each implementation card that adds a key (D2's mapping keys, an OpenAPI datasource kind) is filed at p1.
    • Seam: object-gantt can no longer write back to a same-origin endpoint after #23082 — a measured producer of the retired view-level api source (read + write) #27130 (the customer's gantt api source, route d) is this card's first pull, and its decision is still pending with the maintainer. That decision should be read together with this card's D1 (the code tier first).
    • No state label: needs-user-decision is the state.

    Generated by Claude Code

  2. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #423 item 1 · letters D1 both-in-order · D2 object.external · D3 non-goal · D4 off · D5 off · D6 P1 read-only / P2 pages · maintainer 「立专题卡」 2026-10-28T14:15Z

    Recorded by the director seat (summon #53, session_01QSeKYo7qYsX6HsAneHBMP2, ledger objectstack#12708). The card was filed from the maintainer's own asks in the summon chat (research + deep design) and presented there with six decision points, each with a default; the maintainer answered 「立专题卡」 — the epic form the seat proposed, which takes the defaults (none contested).

    What is ruled

    • D1 both tiers, in order: Tier A (plugin driver, zero protocol change) now as P0–P1; Tier B (declarative OpenAPI datasource) when a second customer asks for a no-code REST source — its children are filed on that pull, not now.
    • D2 Tier B's mapping lives on object.external.operations + object.external.idField (two optional keys, Metadata-protocol: added), the driver's connection shape in its own config contract.
    • D3 cross-datasource joins pushed down stay a non-goal (ADR-0015 §2.2): id-based lookups and in-memory expands only, under the row cap.
    • D4 caching off by default; opt-in TTL per datasource.
    • D5 writes off by default (ADR-0015 double opt-in stands); per operation for Tier B.
    • D6 Studio: read-only field marking in P1; the datasource page and OpenAPI import in P2.
    • Standing rules recorded: the view-level api source stays retired (ruling C on [Decision] Does a view's api data source adopt the page-element rule — a same-origin url and no headers written in metadata? #23082); a driver cannot opt out of correctness — whatever its supports does not claim, the engine applies in memory and refuses above the row cap; credentials only through credentialsRef; object permissions and RLS apply to external objects exactly as to managed ones.

    Execution: this card becomes the epic parent (needs-user-decision → pm:epic; body gains the epic header, the territory and the child tree). Children filed in this act: P0 = the #27130 spike; P1 = the in-memory query helper + row cap + supports enforcement (objectql), the guide (devx), Studio read-only marking (ui) — each Blocked-by: P0. #27130 is ruled d in the same act and waits on P0 and the guide. The maintainer summons /pm-dispatch epic:#27261. Workload as the card: P0 ≈ 0.5 agent-day, P1 ≈ 3–4, P2 ≈ 8–10 when pulled.

  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV

    Epic PM registered; phase plan agreed with the maintainer

    Summoned by the maintainer (田騏, marchtian) with 「pm-dispatch epic:#27261」 in the project thread, 2026-10-28T14:53Z. Before any dispatch the maintainer asked 「先和我讨论分阶段开发计划」; the seat posted the plan below with three questions, and the maintainer answered 「同意」 (2026-10-28T15:01Z, same thread). Source of the three rulings below: who = the maintainer, words = 「同意」 to the plan as posted, where = the project thread of this epic PM session.

    Phases

    Rulings from the maintainer's 「同意」

    1. ui(studio): external-object fields show their datasource and read-only state; no DDL affordances on an external object (epic #27261 P1, D6 first half) #27279 only reads the existing object.external / object.datasource; it does not depend on the spike. Blocked-by: #27276 is removed and it runs beside P0.
    2. 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 maxRows: P1 fixes the row cap at 1000 in the engine, with no authorable key (Metadata-protocol: none). A configurable cap is deferred to P2, to land with the spec keys under the spec seat. Reason: datasource config is parsed against each driver's own config contract (packages/spec/src/data/datasource.zod.ts:577), so an engine-read cross-driver key would be a protocol addition, and the spec seat is vacant.
    3. Lanes: the epic PM writes the children's domain:* from the lanes each card declares (spike(objectql): a virtual object on a plugin driver — external-bound object boots without introspection, reads with filters, formula over current_user, beforeUpdate hook → driver.update, gantt drag writes (epic #27261 P0 · #27130 route d) #27276 / 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 server, docs: "a virtual object — rows from your server, shown in any view": plugin-driver recipe, five business examples, the three-way rule, driver shape-pin test; the #27130 customer reply (epic #27261 P1) #27278 devx, ui(studio): external-object fields show their datasource and read-only state; no DDL affordances on an external object (epic #27261 P1, D6 first half) #27279 ui), with an audit line on each card. Triage's first touch on this epic (6471067191) left the implementation cards to carry their own lanes.

    Concurrency: seat batch 3 (P0 thread + #27279 subagent now).

  4. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Children added · decision cards · session_01J8VVAmeQhJepVXSad145da · 2026-10-28T15:25Z

    Filed on the maintainer's instruction in this session, verbatim: 「你说的问题立决策卡,挂在专题下面」. Both cards carry Epic: #27261 and pm:epic, with needs-user-decision as their state. ⛔ Not a claim; triage sets the lanes.

    P2 (Tier B) — decide before P2's children are filed on the second pull

    Not filed, inherited. Execution identity for virtual-object reads follows ADR-0143 Q1 = A (organization-shared). This card's §5.9 already makes object permissions and RLS the only gate. Under the meta-rule "a new branch reuses the parent ruling", no third card was filed.

    Neither card blocks P0 (#27276) or P1 (#27277 / #27278 / #27279).

    The epic body's ## Children list is the epic PM's to update. This comment is the record until then.

  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8

    Checklist pointer for the epic PM · 2026-10-29T02:29Z · ⛔ not a claim. Two sub-issue decisions of this epic are ruled (batch #426, maintainer 「同意」), and the epic body owes two amendments:

    Both cards close completed with their rulings as content; P2's children cite the ruling comments on Ruling-ref: lines when filed.

  6. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Seat carries · 2026-10-29T09:42Z

    Recorded here, not filed (P3, by pull):

  7. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Seat miss · 2026-10-29T09:57Z

    The epic PM landed PR #27403 (Clause-②: yes, spec ledger rows) and PR #27422 (domain:spec, lint) without a same-shape ## Contract review PASS on their landing heads (contract-review.md, the "落地前检两条" check ①). Each carried an independent full Fable review, but the review was quoted in the seat's ACCEPT prose rather than posted as the record. Per the same reference, both now owe a review on their landing heads:

    PR landing head merged as
    #27403 490c3c331a2132a8b60506d5b82127807822265d 86edcaf8
    #27422 120864f47554b20338ec27a4c5e4a6249ebe8ca4 92297384

    Any FAIL must-fix becomes a new card, not a fix on the closed PR. PR #27476 was pulled out of the queue (6493555143) and waits for its record before landing. From now on the epic PM posts or obtains the same-shape record before every landing stroke.

  8. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Correction to the seat-miss note · 2026-10-29T15:53Z

    Re-read against contract-review.md 〈复核归属与资格〉: whether a contract review is owed depends on the face the diff touches, not on the lane. PR #27422 touches none of the four contract faces: its declaration is Clause-②: no, it has no narrowing arm, it changes no published packages/spec/src/** schema, and it hits no governed path (check-governed-merges: 0 of 11). So it owed no contract-review record, only CI plus the seat's own read, and it does not owe a review after landing.

    The two that do owe a review on their landing heads:

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

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specenhancementNew feature or requestpm:epicpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions