Skip to content

service-datasource: a datasource driver resolves a borrowed connector credential through ADR-0143 D4's per-dispatch resolver (D7 runtime seam) — epic #27261 P2 #27746

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.
Blocked-by: #27742
type: Feature

维护者速读

  • 做什么: 让数据源驱动(先是 OData)在每次请求时通过 ADR-0143 的凭据解析器拿到连接器实例的令牌,过期自动刷新。
  • 为什么: D7 的运行时一半;没有它 spec 的键只是声明。
  • 需要您做的: 无。

Task

  • A seam through which a datasource driver obtains a request credential from the connector instance its datasource names (ADR-0143 D4 per-dispatch resolver, service-automation/src/connector-credentials.ts), without making objectql depend on service-automation (the ruling's open reading: name the dependency direction chosen).
  • driver-odata uses it when the datasource names a connector instance; token refresh on 401 once, then the floor's 503 path.
  • Tests: a borrowed static credential, an oauth2 credential refreshed after expiry, an undeclared instance refused at build (spec side) and at connect.

Clause-②: decided by the dev from the diff · Metadata-protocol: none.

Activity

  1. added
    enhancementNew feature or request
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Oct 29, 2026
  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Claim: PM loop round 10 2026-10-29T23:33Z
    Session: session_017ihL2c5z3q4h8inrn5kwqV (epic PM); implementing card thread session_017ihL2bt3wjGsdgJrX877gc
    Account: marchtian
    Branch: claude/project-thread-wu18sj (the card thread's platform-assigned branch — measured platform exception to claude/issue-<n>-<slug>), merge-based on #27742's branch claude/project-thread-15sb9m (draft PR #27783)
    Worktree: objectstack-issue-27746
    Domain: domain:server
    Seat: epic:#27261#1 (epic PM; no domain:* seat). Maintainer order, 田騏 2026-10-29T21:58Z: 「现在启动全部 P2」 (D7, ruling 6486136301)
    File surface: packages/objectql/src/** (the driver-side credential seam: an interface only, no service-automation import), packages/services/service-datasource/src/** (passing the resolved credential into connect), packages/services/service-automation/src/connector-credentials.ts and its registration (the provider side), their tests, .changeset/** (stop on breach, including any packages/spec/src/** edit beyond #27783's; explain in the report)
    Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: yes
    Metadata-protocol: none
    Responsibility: ruling 6486136301 on #27298 (D7 runtime half), filed by the maintainer's P2 start on epic #27261
    Thread-read: none
    Premise: holds — on origin/main a datasource driver gets credentials only from external.credentialsRef; nothing hands it a connector instance's token.
    Serial constraints: Blocked-by #27742 (draft PR #27783, not landed); this PR lands after it, in the same release. Scope ruling: land the seam with a test-double driver (static credential, oauth2 refreshed after expiry and once on 401, undeclared instance refused at connect). driver-odata (#27741) is not on origin/main; do not merge its branch. Whichever of #27741 / #27746 lands second wires driver-odata to the seam, and the report names which one does it.

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Claim amendment · 2026-10-29T23:34Z

    The card thread measured the card's premise on origin/main (21c4bdd). "ADR-0143 D4's per-dispatch resolver" does not exist yet: ConnectorInstanceAuthSchema is still none | bearer | api-key | basic, and connector-credentials.ts has only resolveConnectorCredential (env → sys_connector_credential → vault, static kinds). D4 is #26702 items 2–4, still open. The scope is narrowed to match.

    Clause-② stays yes.

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Claim amendment 2 · 2026-10-29T23:46Z

    The file surface (amendment 6511046968) adds packages/spec/liveness/datasource.json. The row #27783 added for external.connector as planned says to flip it to live with the connection-service anchor when this server card lands, in the same release. This PR flips it, with evidence in datasource-connection-service.ts (the borrow) and service-automation (the source), and re-records any generated count check:liveness asks for. It still makes no packages/spec/src/** edit.

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Scope addition · 2026-10-30T00:15Z

    One should-fix from the contract review of PR #27788 (record 6511604842) rides this card, because this card edits datasource-connection-service.ts next. The file surface is unchanged.

    • DatasourceConnectionService.reconnect's refusal for a missing previous.driver says a kept pool "would be restored" under a definition naming no driver. On the no-live-pool branch nothing is kept. Reword it to say this happens when a pool is live, and keep the guard first: the type already requires previous, and one rule per parameter is the point.
  6. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

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

    {
     "issue": 27746,
     "status": "done",
     "branch": "claude/project-thread-wu18sj",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/27819",
     "head": "e3d68242",
     "session": "session_017ihL2bt3wjGsdgJrX877gc",
     "premise_still_valid": true,
     "summary": "D7 runtime seam per claim 6511028605 + amendments 6511046968 / 6511235369 / 6511659940. @objectstack/types: IConnectorCredentialSource, ConnectorCredentialHandle (get / refresh, per-dispatch shape), CONNECTOR_CREDENTIAL_SOURCE_SERVICE ('connector-credential-source') and callWithBorrowedCredential (refresh at expiry minus 30 s, refresh once on a 401 result or throw and retry, then 503 SERVICE_UNAVAILABLE naming the instance, never the upstream text). service-automation: createConnectorCredentialSource registered in init() and in providesServices; borrow judges the declaration (readDeclaredConnectorItems) and refuses 422 for undeclared / descriptor / no auth / disabled, 503 when unreadable; a handle resolves auth.credentialRef through the connector's own chain (env, sys_connector_credential row, vault; no store when walled), holds static values until refresh() or a reconcile (credentialGeneration moves on every reconcile, which every credential door write runs), single-flight. service-datasource: borrowConnectorCredential (one copy) used by DatasourceConnectionService.attemptConnect (fail-closed failed-credentials, nothing built), the admin plugin's probe, and DatasourceAdminService create / update-touching-external / testConnection; DatasourceConnectionSpec.credential carries the handle. objectql untouched; no packages/spec/src edit of this card's own. Liveness row external.connector flipped to live, note rewritten per #27783 should-fix f (static kinds resolved once and held; per dispatch only for grant-bearing kinds). reconnect's missing-previous refusal reworded to 'when a pool is live' (guard first) after #27788 landed. Changeset patch on the three runtime packages, Metadata-protocol none.",
     "tests": "On the merged head (origin/main incl. #27788 + #27783 400e1b09), after pnpm install --frozen-lockfile && pnpm build (114/114): types 31 files / 810 tests, service-datasource 64 / 1042, service-automation 219 / 2810, all exit 0; typecheck exit 0 for all three; spec check:generated exit 0; check:liveness exit 0; eslint over the 11 touched files exit 0. New: types connector-credential-source.test.ts (12), service-automation connector-credential-source.test.ts (15, real ObjectQL over SQLite for the stored arm), service-datasource __tests__/datasource-connector-credential.test.ts (14, incl. the disabled-instance case from #27783's review 6511230689). Ablations via scripts/ablation-replace.mjs, each landed and restored (git diff HEAD empty): connect borrow guard off 3530b62bb166 to 401f123de28c (7 red); admin lend refusal off 420e0221f6d0 to bcdabd366b06 (4 red); disabled refusal off 16a766b32331 to ddaf1e5fb677 (1 red); credentialGeneration bump off 8744db630ecd to 6e3897f93f09 (1 red); helper refresh-before-retry off 46ece16a317c to 922e1bb8b425 (6 red).",
     "gates": "dispatch-gates --commands on ded4617e (after merging #27783 400e1b09 and origin/main): 122 derived, all run with exit codes recorded, 122 exit 0 first pass; --ran: 122 derived, 122 run, 0 NOT-MEASURED, 0 UNRUN (derived zero). After the later main merge (#27788): scoped re-check only (rebuild, the three packages' tests and typecheck, spec check:generated and check:liveness). PR CI on e3d68242 at report time: Check Changeset, Lint & Repo Gates, Build Core, Build & E2E UI, Live E2E UI, Governed Surface Queue Guard and Test Core 1,4-8 success; Test Core 2,3, Type Check workspace and Type Check UI in progress.",
     "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
     "mcp_calls": "issue_read (#27746 body + comments, #26702, #27742 comments), pull_request_read (#27788, #27819 check runs), search_pull_requests (#27741/odata), get_job_logs (Check Changeset); no write tool",
     "api_writes": "3 relay writes as objectstack-fleet[bot]: pr_create (draft #27819, assignee marchtian), issue_patch of the #27819 body (Metadata-protocol line), and this os-dev-report comment; plus git pushes of the branch",
     "open_questions": [],
     "deviations": [
      "PR body declares Metadata-protocol: added, not the claim's none: the branch carries #27783's authorable-surface change while #27783 is unlanded, and Check Changeset reds on none (log of job 123822472586). The body says so and that it returns to none after #27783 lands; this card's own changeset stays none / patch. Verified locally with check-changeset-no-major against a payload of the new body, then green in CI.",
      "The relay's issue_patch refuses a body whose first line is not the seat signature, so the #27819 body now opens with the signature line and the claude-projects-attribution block sits on lines 2-3 (pr_create had kept it first)."
     ],
     "out_of_scope_findings": [
      "carrier: #27741 / #27746 (whichever lands second) · driver-odata does not consume DatasourceConnectionSpec.credential yet; wire it to callWithBorrowedCredential",
      "carrier: #26702 · noted, not filed — grant-bearing kinds (oauth2, app-credential, signed) answer through the same handle once ConnectorInstanceAuthSchema declares them; the source's borrowedAuthOf is the place",
      "carrier: none · noted, not filed — a 401 on a borrowed credential does not mark the sys_connector_credential row rejected (the connector call path does); share that bookkeeping only if wanted",
      "carrier: none · noted, not filed — packages/runtime default-datasource-plugin and packages/cli schema-migrate build a DatasourceConnectionService with no lender; an external.connector datasource there is refused loudly naming service-automation (the default cannot carry the key)",
      "carrier: #27783 · its changeset still says the runtime 'ships separately; until it does, the key is validated and not yet read' — stale once both ship in one release",
      "carrier: PM (Skills-docs follow-up owed by #27783) · skills and the virtual-objects / external-datasources guides can now teach external.connector"
     ]
    }
  7. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Claim amendment 3 · 2026-10-30T01:34Z

    #27741 landed first (PR #27796, 0a4b0fb223), so under the claim's serial rule this card wires driver-odata to the seam. The file surface adds packages/drivers/driver-odata/src/**: the credential() provider in plugin.ts that feeds ODataTransport.authorization(), plus a test of the borrowed case. credentialsRef keeps its current path.

  8. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Landed · 2026-10-30T04:29Z

    PR #27819 merged through the queue as ed8b2375a7 on 2026-10-30T04:28:31Z (head 3351ae35). Its first record, 6513636834, was a FAIL; the delta record 6514494060 is a PASS at TIER_DEFAULT, the tier for this face. Seat ACCEPT 6514518972. A datasource that names external.connector now borrows that instance's credential at runtime through the @objectstack/types seam. The connect-time and already-registered refusals fail closed, driver-odata borrows through the call helper, and the liveness row is live. Follow-ons: #27919 (the record's should-fixes) and #27786 (skills and docs), both unblocked now.

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

Metadata

Metadata

Assignees

Labels

area:datadomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queuepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions