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
os serve: a stack's plugin driver resolves its datasource's external.credentialsRef through the host's datasource secret binder (driver-odata on os serve) — epic #27261 P2 #27875
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ① gap found by the #27747 guide carrier (PR #27871, os-dev-report 6513440511) and measured on origin/main.
Ruling-ref: 6486145005 (#27299, Tier B = declarative OData)
Dedupe (open AND closed): "os serve secret binder plugin driver credentialsRef sys_secret" → none this work.
Reader: the epic PM (#27261) dispatches it in the epic subtree.
type: Feature
The host publishes the read half of its datasource secret binder (resolve(credentialsRef)) as a kernel service. driver-odata's plugin uses that service when options.secrets is absent; an explicit options.secrets still wins. Name the service and say whether other plugin drivers should read it the same way.
Fail-closed stays: no service and no options.secrets ⇒ the request is refused, never sent unauthenticated.
Tests: on a host that registers the service, a sys_secret: handle resolves; without it, the refusal is unchanged.
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim: PM loop round 12 2026-10-30T02:29Z
Session: session_017ihL2c5z3q4h8inrn5kwqV (epic PM); implementing card thread session_017ihL2fy9PSGEZJdJpoPvzx
Account: marchtian
Branch: claude/project-thread-vfnybu (the card thread's platform-assigned branch — measured platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27875
Domain: domain:server
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/cli/src/commands/serve.ts (publishing the binder's read half as a kernel service), packages/services/service-datasource/src/** (only if the service name or its type is owned there), packages/types/src/** (only if the read-half type belongs beside the other cross-package seams), packages/drivers/driver-odata/src/plugin.ts (the fallback lookup), content/docs/data-modeling/odata-datasources.mdx (drop the caveat, once #27871 is on main), their tests, .changeset/** (stop on breach, including any packages/spec/src/** edit; explain in the report)
Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: yes
Metadata-protocol: none
Responsibility: epic #27261 P2, gap found by #27747's carrier (os-dev-report 6513440511)
Thread-read: none
Premise: holds — on origin/main, serve.ts builds createDatasourceSecretBinder and hands it only to DatasourceAdminServicePlugin; a stack plugin driver cannot resolve a sys_secret: handle.
Serial constraints: #27746 (PR #27819, in review) edits driver-odata/src/plugin.ts and transport.ts. Merge main after it lands before touching plugin.ts, or keep the edit to the secrets fallback and say how the two meet. #27871 (#27747) owns odata-datasources.mdx until it lands. An explicit options.secrets still wins, and fail-closed is unchanged.
{
"issue": 27875,
"status": "done",
"branch": "claude/project-thread-vfnybu",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27952",
"head": "33bc41fb",
"session": "session_017ihL2fy9PSGEZJdJpoPvzx",
"premise_still_valid": true,
"summary": "Service: DATASOURCE_SECRET_RESOLVER_SERVICE = 'datasource-secret-resolver' with IDatasourceSecretResolver { resolve(credentialsRef) } in packages/types/src/datasource-secret-resolver.ts; the module says every plugin driver dereferencing a credentialsRef reads it the same way (explicit store first, service at the first request never at init, fail-closed). Producer: createDatasourceSecretResolverPlugin(secrets) in cli serve.ts (providesServices the service), mounted after DatasourceAdminServicePlugin only when a binder was built; publishes resolve only, decrypts the datasource_credential scope only. Consumer: driver-odata plugin.ts credential() uses secrets ?? hostSecretResolver(ctx); the kernel's getService throw is caught so the refusal is the plugin's own, now naming the service. Guide: #27871 landed mid-card, so odata-datasources.mdx drops the os serve caveat (mount example without secrets, option row, Credentials section, fail-closed list). Changeset patch on types, cli, driver-odata.",
"tests": "New: cli serve-datasource-secret-resolver.test.ts (5: real ObjectKernel over a real binder with LocalCryptoProvider; bound credential resolves via the service; service keys are only resolve; a settings-scoped row, a missing row and a foreign handle answer undefined; providesServices; source pin of the if (secrets) mount). driver-odata plugin.test.ts (5 new: resolves via the host service, asked once; explicit options.secrets wins and the service is never asked; unresolved handle refused with nothing sent; a service registered after init is seen; on a real kernel with neither, the plugin's own refusal, no kernel text, nothing sent). Ablations each red then restored: fallback removed 3 failed, precedence flipped 1, try/catch removed 1, whole binder registered 1. On 33bc41fb after a full build: driver-odata 720 passed, types 817, cli serve-datasource-* 11; typecheck exit 0 for types, driver-odata, cli.",
"gates": "dispatch-gates --commands on 33bc41fb derived 112 families; all run with exit codes recorded before any pipe; --ran: 112 derived, 112 run, 0 NOT-MEASURED (a DERIVED zero). An earlier pass was red on 3 gates about the new test double (engine-double-contract, where-matcher, cross-package-test-inputs), all fixed in the diff. Check Changeset run locally against the PR body: exit 0. Full battery is CI's.",
"dogfood_battery": "n/a: no ! in the title, no (narrowing) arm",
"mcp_calls": "github issue_read x2 (#27875 get, get_comments), pull_request_read x2 (#27871 get, #27819 get), subscribe_pr_activity x1 (#27952); no MCP write tool",
"api_writes": "2: relay pr_create (run 41005139498, PR #27952, body 10818 bytes read back identical, assignee marchtian); relay comment on #27875 (this report). git push is not a REST write.",
"surface_note": "Outside the claim's file list: scripts/engine-double-contract.pinned.json (the ledger row check:engine-double-contract requires for the new test double; coverage only grows) and pnpm-lock.yaml (+3, the @objectstack/types dependency of driver-odata). No packages/spec/src edit. Worktree is objectstack-27875, not objectstack-issue-27875.",
"serial_constraints": "#27819 (#27746): this PR touches only the if (!secrets) block in credential() plus one helper and one import in plugin.ts, so the two meet as a small textual conflict there; the package.json and lockfile lines for @objectstack/types are identical in both. Whichever lands second resolves it.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: whichever of #27952 / #27819 lands second · packages/drivers/driver-odata/README.md mount example still passes secrets: datasourceSecretBinder (still correct, now optional under os serve); one-line follow-up, README outside this claim",
"note: a stack that registers its own datasource-secret-resolver service collides loudly at boot with os serve's (Service already registered); os serve mounts its plugin only when it mounts the datasource admin itself"
]
}
PR #27952 merged through the queue as 0a52b7dd on 2026-10-30T06:19:49Z. Head was cf00041b. The contract review PASS is 6515948918 at TIER_DEFAULT, on the declaration face, and the seat ACCEPT followed it. Under os serve, a plugin driver's datasource credentialsRef now resolves through the server's own sys_secret store, using the kernel service datasource-secret-resolver (@objectstack/types). An OData stack needs no secrets option. The driver-odata README and the OData guide say so.
Activity
objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim: PM loop round 12 2026-10-30T02:29Z
Session:
session_017ihL2c5z3q4h8inrn5kwqV(epic PM); implementing card threadsession_017ihL2fy9PSGEZJdJpoPvzxAccount:
marchtianBranch:
claude/project-thread-vfnybu(the card thread's platform-assigned branch — measured platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27875Domain:
domain:serverSeat:
epic:#27261#1(epic PM; nodomain:*seat). Maintainer order, 田騏 2026-10-29T21:58Z: 「现在启动全部 P2」 (ruling 6486145005, Tier B = declarative OData)File surface:
packages/cli/src/commands/serve.ts(publishing the binder's read half as a kernel service),packages/services/service-datasource/src/**(only if the service name or its type is owned there),packages/types/src/**(only if the read-half type belongs beside the other cross-package seams),packages/drivers/driver-odata/src/plugin.ts(the fallback lookup),content/docs/data-modeling/odata-datasources.mdx(drop the caveat, once #27871 is onmain), their tests,.changeset/**(stop on breach, including anypackages/spec/src/**edit; explain in the report)Container & model:
S,mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: yes
Metadata-protocol: none
Responsibility: epic #27261 P2, gap found by #27747's carrier (os-dev-report 6513440511)
Thread-read: none
Premise: holds — on
origin/main,serve.tsbuildscreateDatasourceSecretBinderand hands it only toDatasourceAdminServicePlugin; a stack plugin driver cannot resolve asys_secret:handle.Serial constraints: #27746 (PR #27819, in review) edits
driver-odata/src/plugin.tsandtransport.ts. Mergemainafter it lands before touchingplugin.ts, or keep the edit to thesecretsfallback and say how the two meet. #27871 (#27747) ownsodata-datasources.mdxuntil it lands. An explicitoptions.secretsstill wins, and fail-closed is unchanged.objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2fy9PSGEZJdJpoPvzx
os-dev-report
{ "issue": 27875, "status": "done", "branch": "claude/project-thread-vfnybu", "pr": "https://github.com/objectstack-ai/objectstack/pull/27952", "head": "33bc41fb", "session": "session_017ihL2fy9PSGEZJdJpoPvzx", "premise_still_valid": true, "summary": "Service: DATASOURCE_SECRET_RESOLVER_SERVICE = 'datasource-secret-resolver' with IDatasourceSecretResolver { resolve(credentialsRef) } in packages/types/src/datasource-secret-resolver.ts; the module says every plugin driver dereferencing a credentialsRef reads it the same way (explicit store first, service at the first request never at init, fail-closed). Producer: createDatasourceSecretResolverPlugin(secrets) in cli serve.ts (providesServices the service), mounted after DatasourceAdminServicePlugin only when a binder was built; publishes resolve only, decrypts the datasource_credential scope only. Consumer: driver-odata plugin.ts credential() uses secrets ?? hostSecretResolver(ctx); the kernel's getService throw is caught so the refusal is the plugin's own, now naming the service. Guide: #27871 landed mid-card, so odata-datasources.mdx drops the os serve caveat (mount example without secrets, option row, Credentials section, fail-closed list). Changeset patch on types, cli, driver-odata.", "tests": "New: cli serve-datasource-secret-resolver.test.ts (5: real ObjectKernel over a real binder with LocalCryptoProvider; bound credential resolves via the service; service keys are only resolve; a settings-scoped row, a missing row and a foreign handle answer undefined; providesServices; source pin of the if (secrets) mount). driver-odata plugin.test.ts (5 new: resolves via the host service, asked once; explicit options.secrets wins and the service is never asked; unresolved handle refused with nothing sent; a service registered after init is seen; on a real kernel with neither, the plugin's own refusal, no kernel text, nothing sent). Ablations each red then restored: fallback removed 3 failed, precedence flipped 1, try/catch removed 1, whole binder registered 1. On 33bc41fb after a full build: driver-odata 720 passed, types 817, cli serve-datasource-* 11; typecheck exit 0 for types, driver-odata, cli.", "gates": "dispatch-gates --commands on 33bc41fb derived 112 families; all run with exit codes recorded before any pipe; --ran: 112 derived, 112 run, 0 NOT-MEASURED (a DERIVED zero). An earlier pass was red on 3 gates about the new test double (engine-double-contract, where-matcher, cross-package-test-inputs), all fixed in the diff. Check Changeset run locally against the PR body: exit 0. Full battery is CI's.", "dogfood_battery": "n/a: no ! in the title, no (narrowing) arm", "mcp_calls": "github issue_read x2 (#27875 get, get_comments), pull_request_read x2 (#27871 get, #27819 get), subscribe_pr_activity x1 (#27952); no MCP write tool", "api_writes": "2: relay pr_create (run 41005139498, PR #27952, body 10818 bytes read back identical, assignee marchtian); relay comment on #27875 (this report). git push is not a REST write.", "surface_note": "Outside the claim's file list: scripts/engine-double-contract.pinned.json (the ledger row check:engine-double-contract requires for the new test double; coverage only grows) and pnpm-lock.yaml (+3, the @objectstack/types dependency of driver-odata). No packages/spec/src edit. Worktree is objectstack-27875, not objectstack-issue-27875.", "serial_constraints": "#27819 (#27746): this PR touches only the if (!secrets) block in credential() plus one helper and one import in plugin.ts, so the two meet as a small textual conflict there; the package.json and lockfile lines for @objectstack/types are identical in both. Whichever lands second resolves it.", "open_questions": [], "out_of_scope_findings": [ "carrier: whichever of #27952 / #27819 lands second · packages/drivers/driver-odata/README.md mount example still passes secrets: datasourceSecretBinder (still correct, now optional under os serve); one-line follow-up, README outside this claim", "note: a stack that registers its own datasource-secret-resolver service collides loudly at boot with os serve's (Service already registered); os serve mounts its plugin only when it mounts the datasource admin itself" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Landed · 2026-10-30T06:34Z
PR #27952 merged through the queue as
0a52b7ddon 2026-10-30T06:19:49Z. Head wascf00041b. The contract review PASS is 6515948918 atTIER_DEFAULT, on the declaration face, and the seat ACCEPT followed it. Underos serve, a plugin driver's datasourcecredentialsRefnow resolves through the server's ownsys_secretstore, using the kernel servicedatasource-secret-resolver(@objectstack/types). An OData stack needs nosecretsoption. The driver-odata README and the OData guide say so.