Skip to content

feat(datasource): reject field.columnName on external + drop showcase onEnable bridge (ADR-0062 Phase 4, D7/D8)#2203

Merged
os-zhuang merged 5 commits into
feat/adr-0062-credentials-p2from
feat/adr-0062-reconcile-cleanup-p4
Jun 22, 2026
Merged

feat(datasource): reject field.columnName on external + drop showcase onEnable bridge (ADR-0062 Phase 4, D7/D8)#2203
os-zhuang merged 5 commits into
feat/adr-0062-credentials-p2from
feat/adr-0062-reconcile-cleanup-p4

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

ADR-0062 Phase 4 — reconciliation + cleanup (D7, D8)

Stacked on #2200 (Phase 3) → #2199#2198. Merge those first.

D7 — columnMap is authoritative; reject field.columnName on external objects

os compile/build (validateStackExpressions) now errors when a federated object (one declaring an external binding) has a field with columnName. The driver's query pipeline does not apply field.columnName for external objects — external.columnMap (remoteColumn → localField) is the single source of truth — so allowing both is a silent dual-source ambiguity. The error message points to external.columnMap. Managed objects are untouched.

D8 — drop the onEnable driver bridge from the canonical example

examples/app-showcase now declares its external datasource with no onEnable driver registration. onEnable only provisions the "remote" fixture tables; the declared datasource auto-connects at boot (Phase 1, D1). onEnable + ctx.drivers.register stays documented as the escape hatch for drivers built dynamically at runtime.

To cover this end-to-end in the dogfood gate, the @objectstack/verify harness now wires the datasource-admin plugin (registering 'datasource-connection') when an app declares datasources — so the harness mirrors objectstack dev/serve. Previously the harness imported only an app's default export, so onEnable never ran there and external objects were never read; now auto-connect handles the driver and a new dogfood test reads the federated objects through the real REST stack.

Tests / validation

  • D7: 4 unit tests (external field.columnName → error; map-shaped field; managed untouched; clean external passes). cli validate-expressions suite green (29).
  • D8: new showcase-external-autoconnect.dogfood.test.ts — reads showcase_ext_customer/_order (incl. remoteName remap customers/orders + ?region=EU pushdown), with no onEnable driver wiring. ✅
  • Local CLI dogfood: objectstack verify --app examples/app-showcase --rls ✅ and --app examples/app-crm --rls ✅ (D2 gate keeps app-crm metadata-only — byte-for-byte unchanged under the harness change).

Closes #2163 — Phase 4 (final functional phase; the default-driver bootstrap refactor is the remaining standalone follow-up).

🤖 Generated with Claude Code

os-zhuang and others added 5 commits June 22, 2026 19:12
… (ADR-0062 Phase 1)

Declare an external datasource → it auto-connects to a live ObjectQL driver and
its federated objects are queryable with ZERO app code (no onEnable). Implements
ADR-0062 Phase 1 (D1/D2/D5) toward epic #2163.

D1 — one connect path: new DatasourceConnectionService owns the single
"definition → live driver" path (factory build → credentialsRef resolve →
connect → registerDriver under the datasource name → registerDatasourceDef →
DDL-free syncObjectSchema per bound object). The runtime-admin registerPool now
delegates to it; AppPlugin auto-connects code-defined datasources. Exposed as the
'datasource-connection' kernel service.

D2 — opt-in-safe gate: connect only when external, an object explicitly binds via
object.datasource, or autoConnect:true. Managed datasources referenced only by a
datasourceMapping rule (e.g. app-crm's :memory: datasources) stay metadata-only —
existing apps byte-for-byte unchanged. Adds datasource.autoConnect to the spec.

D5 — lifecycle/ordering/policy: connect in AppPlugin.start() before the
kernel:ready validation gate (init-all-then-start-all). Fail-fast for declared
external + onMismatch:'fail'; degrade otherwise (always for runtime-admin/
rehydrate). New host-injectable DatasourceConnectPolicy (open-core default allows;
multi-tenant host binds a stricter fail-closed policy) consulted before connect.

Tests: 15 connection-service unit tests + 5 runtime integration tests (auto-connect,
managed-unrouted stays metadata-only, queryable end-to-end, deny policy). onEnable +
ctx.drivers.register remains a supported, idempotent escape hatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…TS build)

The context logger interface types error(message, error?: Error) — passing a meta
object {appId, error} tripped the tsup DTS build (TS2353) even though tsc --noEmit
passed. Switch to a single interpolated message; the rethrow still surfaces the
real cause to the kernel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…062 Phase 2, D3)

A declared external.credentialsRef MUST resolve to a cleartext secret before the
driver is built — an absent secret store or an unresolvable/undecryptable ref now
fails closed (clear message, datasource left unconnected) instead of silently
building a driver without the credential. Follows the same fail-fast (declared
external + onMismatch:fail) vs degrade policy as connect failures. Converges with
the runtime-admin secret path (same SecretBinder threaded through the shared
connection service).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ADR-0062 Phase 3, D6)

NativeSQLStrategy hand-compiles FROM/columns that bypass the driver's physical
resolution (remoteName/remoteSchema/columnMap), so it would aggregate against the
wrong table for a federated object. It now declines any query whose base or joined
object is external (new optional StrategyContext.isExternalObject hook, reported by
the analytics plugin from the object's external block), routing it to the
ObjectQLStrategy whose engine.aggregate goes through the driver's getBuilder
(#2138/#2149). Reuses the driver's resolution rather than re-implementing it; until
a native-SQL fast path exists, external analytics is correct (via ObjectQL) instead
of silently wrong.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…owcase onEnable bridge (ADR-0062 Phase 4)

D7: os compile/build (validateStackExpressions) rejects field.columnName on a
federated object — external.columnMap is authoritative; managed objects untouched.

D8: examples/app-showcase declares its external datasource with NO onEnable driver
registration; the declared datasource auto-connects (D1). onEnable now only
provisions the remote fixture tables. The @objectstack/verify harness wires the
datasource-admin plugin (→ datasource-connection service) when an app declares
datasources, mirroring os dev/serve, so a new dogfood test exercises the federated
read end-to-end (incl. remoteName remap + filter pushdown). onEnable +
ctx.drivers.register stays supported as the dynamic-driver escape hatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 22, 2026 11:54am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling size/m labels Jun 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/dogfood, @objectstack/verify.

15 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/cloud-artifact-api.mdx (via packages/cli)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/cli)
  • content/docs/concepts/packages.mdx (via @objectstack/cli)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/cli)
  • content/docs/guides/authentication.mdx (via @objectstack/cli)
  • content/docs/guides/client-sdk.mdx (via @objectstack/cli)
  • content/docs/guides/hook-bodies.mdx (via packages/cli)
  • content/docs/guides/packages.mdx (via @objectstack/cli)
  • content/docs/guides/project-scoping.mdx (via @objectstack/cli)
  • content/docs/guides/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/guides/runtime-services/index.mdx (via packages/cli)
  • content/docs/guides/skills.mdx (via packages/cli)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/feat/adr-0062-analytics-external-p3 → pass the list as args.docs.

Base automatically changed from feat/adr-0062-analytics-external-p3 to feat/adr-0062-credentials-p2 June 22, 2026 12:20
@os-zhuang
os-zhuang merged commit 595c917 into feat/adr-0062-credentials-p2 Jun 22, 2026
8 checks passed
@os-zhuang
os-zhuang deleted the feat/adr-0062-reconcile-cleanup-p4 branch June 22, 2026 12:21
xuyushun441-sys added a commit that referenced this pull request Jun 22, 2026
…062 Phase 2, D3) (#2199)

* feat(datasource): fail-closed credential resolution at connect (ADR-0062 Phase 2, D3)

A declared external.credentialsRef MUST resolve to a cleartext secret before the
driver is built — an absent secret store or an unresolvable/undecryptable ref now
fails closed (clear message, datasource left unconnected) instead of silently
building a driver without the credential. Follows the same fail-fast (declared
external + onMismatch:fail) vs degrade policy as connect failures. Converges with
the runtime-admin secret path (same SecretBinder threaded through the shared
connection service).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(analytics): analytics over federated objects honors the remote table (ADR-0062 Phase 3, D6) (#2200)

* feat(datasource): reject field.columnName on external + drop showcase onEnable bridge (ADR-0062 Phase 4, D7/D8) (#2203)

---------

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants