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. 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.

  2. 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.

  3. 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.

  4. 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.
  5. 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"
     ]
    }
  6. 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.

  7. 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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions