You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
datasource import: generate object drafts from an OData $metadata document, and the Studio datasource page lists and imports its entity sets — epic #27261 P2 #27745
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.
Blocked-by: #27741
type: Feature
维护者速读
做什么: 在 Studio 里指向一个 OData 数据源,列出它的实体集,勾选后生成对象草稿(字段、类型、主键、关联);AI 通过 MCP 的同一入口也能做。
为什么: 不写代码接入的另一半:没有导入,管理员仍要手写对象。
需要您做的: 无。
Task
IExternalDatasourceService.generateObjectDraft / importObject work for an odata datasource through the driver's introspectSchema ($metadata): EDM types map to field types, the key to the id, navigation properties to lookups when the target is also imported (else left out and named).
Studio's datasource page (ui/packages/app-shell/src/views/metadata-admin) lists an OData datasource's entity sets with an import action; imported objects are external, read-only unless the author opts in.
MCP: list_remote_tables / import_object reach the same path.
Tests: draft generation from a recorded $metadata fixture; the Studio flow against a mocked service.
Clause-②: decided by the dev from the diff · Metadata-protocol: none.
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim: PM loop round 11 2026-10-30T01:37Z
Session: session_017ihL2c5z3q4h8inrn5kwqV (epic PM); implementing card thread session_017ihL2c8AWH1pFbfa5XHrbg
Account: marchtian
Branch: claude/project-thread-kmqsti (the card thread's platform-assigned branch — measured platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27745
Domain: domain:ui
Seat: epic:#27261#1 (epic PM; no domain:* seat). Maintainer order, 田騏 2026-10-29T21:58Z: 「现在启动全部 P2」 (ruling 6486145005, Tier B = declarative OData)
File surface: packages/services/service-datasource/src/** (draft generation and import for a driver.odata datasource through introspectSchema), packages/drivers/driver-odata/src/** (only what introspectSchema must add for drafts: EDM → field type, key, navigation), ui/packages/app-shell/src/views/metadata-admin/** (the datasource page's entity-set list and import action), the MCP list_remote_tables / import_object path, their tests, .changeset/** (stop on breach, including any packages/spec/src/** edit; explain in the report)
Container & model: M, 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), unblocked by #27741 (PR #27796, 0a4b0fb223)
Thread-read: none
Premise: holds — on origin/main (0a4b0fb2) driver-odata answers introspectSchema from $metadata, but no draft or import path or Studio flow reaches an OData datasource.
Serial constraints: #27746 (PR #27819) edits service-datasource/src/datasource-connection-service.ts and the admin plugin/service; do not edit those files, or merge main after #27819 lands and say so. If the diff turns out to change a published contract (Clause-② other than no), stop and report before the PR.
The card thread stopped on a spec question, as the claim asks. On main, the type-compat matrix (packages/spec/src/data/type-compat.ts) knows no EDM names. It serves both draft suggestion (suggestFieldTypeForSqlType) and Gate 2 boot validation (isCompatible). So every Edm.* column canonicalises to unknown: drafts come out all text, and an object mapping Edm.Int32 to number is a type_mismatch error at boot under onMismatch: 'fail'.
PM ruling: option A. The matrix gains an odata arm: SqlDialect adds 'odata', and DIALECT_ALIASES.odata maps the EDM primitives, with Edm.Int64 and Edm.Decimal lossy as their SQL kin are. The service maps the driver's odata-4.0 dialect onto it. This keeps one type table for draft, catalog and Gate 2. A second EDM table inside service-datasource (option B) was declined: it would leave the CLI and every other matrix reader EDM-blind.
File surface adds packages/spec/src/data/type-compat.ts, its tests, and the generated references the change re-records.
Clause-② becomes yes: a published union widens. The PR names every exhaustive consumer of SqlDialect in the repository, ui/ included, and updates it in the same PR.
Contract face: the PR body says "Fable review owed". It lands only after a CONTRACT_REVIEW_TIER record PASS on its head.
Navigation lookups read the registry (no contract change). The draft's snake_case field names with external.columnMap for PascalCase EDM properties stay inside service-datasource.
Second half of the option-A ruling (6512853588). isCompatible lists lookup only under the uuid row, so a lookup over a foreign-key column of type Edm.Int32, int or varchar is a Gate 2 type_mismatch today, for SQL federation too. The same file adds lookup and master_detail to the exact sets of the text, integer and bigint rows. Gate 2 gets looser and nothing that passes today fails. The changeset and PR say so in plain words for SQL federation.
A single-valued navigation becomes a lookup only when it has a ReferentialConstraint whose dependent property is on the entity type and whose referenced property is the target's single key. Every other navigation is left out and named in the review notes. No further surface change.
#27747's guide (PR #27871) says in content/docs/data-modeling/odata-datasources.mdx §2: "Declare each field by hand. Nothing generates fields from $metadata today." When this card's import lands, rewrite that bullet to point at the import flow (Studio datasource page and MCP import_object). The file surface adds that one page. Merge main after #27871 lands.
{
"issue": 27745,
"status": "done",
"branch": "claude/project-thread-kmqsti",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27880",
"head": "14e38cf6",
"session": "session_017ihL2c8AWH1pFbfa5XHrbg",
"premise_still_valid": true,
"summary": "OData $metadata import end to end. spec type-compat (ruling A, amendments 6512853588/6512860835): SqlDialect 'odata' with EDM aliases; lookup/master_detail exact under text/integer/bigint (Gate 2 looser for SQL federation too, Clause-② yes widening). driver-odata introspectSchema lists each navigation's referentialConstraints. service-datasource: typeVocabulary maps odata-4.0 to odata for draft/refreshCatalog/validate; snake_case field and object names with external.columnMap (rename overrides land there too; fixes the _products object name); draftLookups makes a lookup only for a single ReferentialConstraint onto the target's single key with the target imported on the same datasource (or self), every other navigation left out and named in review. Studio tables tab: multi-select + Import selected (targets first, by review remoteType), batch results, write opt-in only when the datasource allows writes (single-table dialog too), en/zh rows. MCP: registerFederationTools list_remote_tables/import_object under grantsAuthoringTools; runtime bridge asks FEDERATION_READ/WRITE_CAPABILITY before the service lookup (403), 503 without a service; rest exports the two constants. Guide odata-datasources.mdx §2 bullet rewritten to point at the import (PM item after #27871).",
"tests": "service-datasource 64 files / 1041 passed incl. external-object-draft-odata-metadata.test.ts (13: real ODataDriver over a recorded Northwind $metadata, EDM mapping, columnMap, lookups only onto imported targets, self ref, composite source, Gate 2 clean on 4 objects + negative control, driver follows category_id). driver-odata 439 passed; spec type-compat 27 passed, check:generated current; mcp 595 passed incl. mcp-federation-tools.test.ts (9); runtime mcp-federation-bridge.test.ts (4) + MCP domain files; ui metadata-admin external+previews 154 files / 1722 passed incl. SchemaBrowser.batchImport-27745.test.tsx (7); app-shell type-check 0.",
"gates": "dispatch-gates --commands after merging origin/main (e320e366) and the guide edit: 122 derived. First pass on 100: 93 exit 0, 7 exit 3 (teardown fixture commit, spec dist, dual-build dist); all rerun after their prerequisites, exit 0; the 22 families the merge/docs added all exit 0. Final --ran: 122 derived, 122 run, 0 NOT-MEASURED (DERIVED zero). CI on 4f044127 all green; on 14e38cf6 at report time: lint/type-check source gates, Check Changeset, PR-body guards success, Test Core and builds in_progress, 0 failure.",
"dogfood_battery": "n/a: no ! in the title; Dogfood Regression Gate job green on the PR",
"mcp_calls": "github issue_read (#27745 get/comments), pull_request_read (#27880 get, get_check_runs), get_job_logs (the test-source alias gate red on bc74ebf9), subscribe_pr_activity x1; no MCP write tool",
"api_writes": "relay pr_create (PR #27880); relay issue_patch on #27880 (run 40996633151, 11703 bytes read back identical); relay comment on #27745 (this report). git push is not a REST write.",
"surface_note": "No edit to datasource-connection-service.ts or the admin plugin/service (#27819), per the Claim. packages/spec touched under the PM ruling; PR says Fable review owed and waits for a contract review PASS. vitest.config.ts alias for @objectstack/driver-odata (test-source alias gate). content/docs/data-modeling/odata-datasources.mdx per the PM's added item.",
"deviation": "The ruling text said Edm.Int64/Edm.Decimal would be lossy; they take SQL's bigint/numeric verdict (exact for number), because lossiness is per canonical row. My misstatement in the option text; stated in the PR body.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · runtime test-layer typecheck reports @objectstack/driver-turso unresolved in organization-ownership-boot-guard.turso-remote.kernel.test.ts in a cloud container (package not built); CI green, untouched file, not filed"
]
}
PR #27880 merged through the queue as d3b0a405d9 on 2026-10-30T04:21:18Z (head 14e38cf6; contract review PASS 6514424141 at CONTRACT_REVIEW_TIER, which, under the 2026-10-29 19:39Z order, also served as the full review of the spec change; seat ACCEPT 6514449474). An OData datasource's entity sets import as object drafts from $metadata through Studio's Tables tab and MCP list_remote_tables / import_object. The type matrix knows the odata dialect, and a lookup can sit on a text or integer foreign key. Follow-on: #27917 (the record's should-fixes).
Activity
objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim: PM loop round 11 2026-10-30T01:37Z
Session:
session_017ihL2c5z3q4h8inrn5kwqV(epic PM); implementing card threadsession_017ihL2c8AWH1pFbfa5XHrbgAccount:
marchtianBranch:
claude/project-thread-kmqsti(the card thread's platform-assigned branch — measured platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27745Domain:
domain:uiSeat:
epic:#27261#1(epic PM; nodomain:*seat). Maintainer order, 田騏 2026-10-29T21:58Z: 「现在启动全部 P2」 (ruling 6486145005, Tier B = declarative OData)File surface:
packages/services/service-datasource/src/**(draft generation and import for adriver.odatadatasource throughintrospectSchema),packages/drivers/driver-odata/src/**(only whatintrospectSchemamust add for drafts: EDM → field type, key, navigation),ui/packages/app-shell/src/views/metadata-admin/**(the datasource page's entity-set list and import action), the MCPlist_remote_tables/import_objectpath, their tests,.changeset/**(stop on breach, including anypackages/spec/src/**edit; explain in the report)Container & model:
M,mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: epic #27261 P2 (ruling 6486145005), unblocked by #27741 (PR #27796,
0a4b0fb223)Thread-read: none
Premise: holds — on
origin/main(0a4b0fb2)driver-odataanswersintrospectSchemafrom$metadata, but no draft or import path or Studio flow reaches an OData datasource.Serial constraints: #27746 (PR #27819) edits
service-datasource/src/datasource-connection-service.tsand the admin plugin/service; do not edit those files, or mergemainafter #27819 lands and say so. If the diff turns out to change a published contract (Clause-② other thanno), stop and report before the PR.objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim amendment · 2026-10-30T01:41Z
The card thread stopped on a spec question, as the claim asks. On
main, the type-compat matrix (packages/spec/src/data/type-compat.ts) knows no EDM names. It serves both draft suggestion (suggestFieldTypeForSqlType) and Gate 2 boot validation (isCompatible). So everyEdm.*column canonicalises tounknown: drafts come out alltext, and an object mappingEdm.Int32tonumberis atype_mismatcherror at boot underonMismatch: 'fail'.PM ruling: option A. The matrix gains an
odataarm:SqlDialectadds'odata', andDIALECT_ALIASES.odatamaps the EDM primitives, withEdm.Int64andEdm.Decimallossy as their SQL kin are. The service maps the driver'sodata-4.0dialect onto it. This keeps one type table for draft, catalog and Gate 2. A second EDM table insideservice-datasource(option B) was declined: it would leave the CLI and every other matrix reader EDM-blind.packages/spec/src/data/type-compat.ts, its tests, and the generated references the change re-records.yes: a published union widens. The PR names every exhaustive consumer ofSqlDialectin the repository,ui/included, and updates it in the same PR.CONTRACT_REVIEW_TIERrecord PASS on its head.Navigation lookups read the registry (no contract change). The draft's snake_case field names with
external.columnMapfor PascalCase EDM properties stay insideservice-datasource.objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim amendment 2 · 2026-10-30T01:41Z
Second half of the option-A ruling (6512853588).
isCompatiblelistslookuponly under theuuidrow, so a lookup over a foreign-key column of typeEdm.Int32,intorvarcharis a Gate 2type_mismatchtoday, for SQL federation too. The same file addslookupandmaster_detailto the exact sets of the text, integer and bigint rows. Gate 2 gets looser and nothing that passes today fails. The changeset and PR say so in plain words for SQL federation.A single-valued navigation becomes a lookup only when it has a
ReferentialConstraintwhose dependent property is on the entity type and whose referenced property is the target's single key. Every other navigation is left out and named in the review notes. No further surface change.objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Scope addition · 2026-10-30T02:43Z
#27747's guide (PR #27871) says in
content/docs/data-modeling/odata-datasources.mdx§2: "Declare each field by hand. Nothing generates fields from$metadatatoday." When this card's import lands, rewrite that bullet to point at the import flow (Studio datasource page and MCPimport_object). The file surface adds that one page. Mergemainafter #27871 lands.objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c8AWH1pFbfa5XHrbg
os-dev-report
{ "issue": 27745, "status": "done", "branch": "claude/project-thread-kmqsti", "pr": "https://github.com/objectstack-ai/objectstack/pull/27880", "head": "14e38cf6", "session": "session_017ihL2c8AWH1pFbfa5XHrbg", "premise_still_valid": true, "summary": "OData $metadata import end to end. spec type-compat (ruling A, amendments 6512853588/6512860835): SqlDialect 'odata' with EDM aliases; lookup/master_detail exact under text/integer/bigint (Gate 2 looser for SQL federation too, Clause-② yes widening). driver-odata introspectSchema lists each navigation's referentialConstraints. service-datasource: typeVocabulary maps odata-4.0 to odata for draft/refreshCatalog/validate; snake_case field and object names with external.columnMap (rename overrides land there too; fixes the _products object name); draftLookups makes a lookup only for a single ReferentialConstraint onto the target's single key with the target imported on the same datasource (or self), every other navigation left out and named in review. Studio tables tab: multi-select + Import selected (targets first, by review remoteType), batch results, write opt-in only when the datasource allows writes (single-table dialog too), en/zh rows. MCP: registerFederationTools list_remote_tables/import_object under grantsAuthoringTools; runtime bridge asks FEDERATION_READ/WRITE_CAPABILITY before the service lookup (403), 503 without a service; rest exports the two constants. Guide odata-datasources.mdx §2 bullet rewritten to point at the import (PM item after #27871).", "tests": "service-datasource 64 files / 1041 passed incl. external-object-draft-odata-metadata.test.ts (13: real ODataDriver over a recorded Northwind $metadata, EDM mapping, columnMap, lookups only onto imported targets, self ref, composite source, Gate 2 clean on 4 objects + negative control, driver follows category_id). driver-odata 439 passed; spec type-compat 27 passed, check:generated current; mcp 595 passed incl. mcp-federation-tools.test.ts (9); runtime mcp-federation-bridge.test.ts (4) + MCP domain files; ui metadata-admin external+previews 154 files / 1722 passed incl. SchemaBrowser.batchImport-27745.test.tsx (7); app-shell type-check 0.", "gates": "dispatch-gates --commands after merging origin/main (e320e366) and the guide edit: 122 derived. First pass on 100: 93 exit 0, 7 exit 3 (teardown fixture commit, spec dist, dual-build dist); all rerun after their prerequisites, exit 0; the 22 families the merge/docs added all exit 0. Final --ran: 122 derived, 122 run, 0 NOT-MEASURED (DERIVED zero). CI on 4f044127 all green; on 14e38cf6 at report time: lint/type-check source gates, Check Changeset, PR-body guards success, Test Core and builds in_progress, 0 failure.", "dogfood_battery": "n/a: no ! in the title; Dogfood Regression Gate job green on the PR", "mcp_calls": "github issue_read (#27745 get/comments), pull_request_read (#27880 get, get_check_runs), get_job_logs (the test-source alias gate red on bc74ebf9), subscribe_pr_activity x1; no MCP write tool", "api_writes": "relay pr_create (PR #27880); relay issue_patch on #27880 (run 40996633151, 11703 bytes read back identical); relay comment on #27745 (this report). git push is not a REST write.", "surface_note": "No edit to datasource-connection-service.ts or the admin plugin/service (#27819), per the Claim. packages/spec touched under the PM ruling; PR says Fable review owed and waits for a contract review PASS. vitest.config.ts alias for @objectstack/driver-odata (test-source alias gate). content/docs/data-modeling/odata-datasources.mdx per the PM's added item.", "deviation": "The ruling text said Edm.Int64/Edm.Decimal would be lossy; they take SQL's bigint/numeric verdict (exact for number), because lossiness is per canonical row. My misstatement in the option text; stated in the PR body.", "open_questions": [], "out_of_scope_findings": [ "carrier: none · runtime test-layer typecheck reports @objectstack/driver-turso unresolved in organization-ownership-boot-guard.turso-remote.kernel.test.ts in a cloud container (package not built); CI green, untouched file, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Landed · 2026-10-30T04:31Z
PR #27880 merged through the queue as
d3b0a405d9on 2026-10-30T04:21:18Z (head14e38cf6; contract review PASS 6514424141 atCONTRACT_REVIEW_TIER, which, under the 2026-10-29 19:39Z order, also served as the full review of the spec change; seat ACCEPT 6514449474). An OData datasource's entity sets import as object drafts from$metadatathrough Studio's Tables tab and MCPlist_remote_tables/import_object. The type matrix knows theodatadialect, and a lookup can sit on a text or integer foreign key. Follow-on: #27917 (the record's should-fixes).