feat(engine): data-plane embedder enablers (engine registry + type-routing resolver)#387
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an engine registry extension seam so embedders can supply engine.Engine implementations for database types that SchemaBot’s core build doesn’t ship, while keeping built-in mysql (Spirit) and vitess (PlanetScale) behavior unchanged.
Changes:
- Introduces
tern.EngineFactoryand wiresLocalConfig.EngineFactoriesintotern.NewLocalClientto build a custom engine for non-built-in database types. - Adds
api.Service.RegisterEngine(databaseType, factory)to let embedders register factories that flow into service-built local Tern clients. - Adds unit tests covering registered/unregistered factories and failure-closed behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| pkg/tern/local_client.go | Adds EngineFactory + config plumbing and a customEngine path for non-built-in database types. |
| pkg/tern/local_client_test.go | Adds tests for registry-based engine construction and failure-closed behaviors. |
| pkg/api/service.go | Stores engine factories on the service and injects them into local client construction; adds RegisterEngine. |
| pkg/api/service_test.go | Adds a test validating that RegisterEngine enables local client creation for a custom database type. |
Comments suppressed due to low confidence (1)
pkg/tern/local_client_test.go:2385
- Now that NewLocalClient fails closed when a factory returns a nil engine, it should also fail closed (with a test) when the registry contains the key but the EngineFactory function itself is nil. Without this, a nil factory would currently panic at call time.
require.Error(t, err)
assert.Contains(t, err.Error(), "nil engine")
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1ee45b1 to
18a36a3
Compare
The data plane builds one engine per database type, but the set was hardcoded (mysql -> Spirit, vitess -> PlanetScale), so an embedding service had no way to drive a database type this build does not include. Add an extension point behind the existing engine.Engine interface: - tern.EngineFactory + LocalConfig.EngineFactories: NewLocalClient builds the engine for a type without a built-in engine from a registered factory, and fails closed when a type has no engine, the registered factory is nil, the factory errors, or it returns a nil engine. Built-in mysql/vitess unchanged. - api.Service.RegisterEngine(databaseType, factory) is the embedder hook; it validates its inputs and returns an error so misconfiguration fails fast. - protoEngine reports the engine actually backing the client (via its Name) so a registered engine is not misreported as the Spirit default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…anes An engine resolver serves a single engine — it rejects a request whose database type it does not serve — so a data plane that serves more than one engine had no way to compose them behind the one Resolver a TargetRouter takes. TypeRoutingResolver routes a request to the resolver registered for its database type, failing closed when the request carries no type or no resolver is registered for it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
18a36a3 to
c83395e
Compare
morgo
approved these changes
Jun 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why this matters
Running the data plane in an embedding service across more than one engine — including a database type this build does not ship — needs two extension points the core lacked: a way to supply a custom engine, and a way to compose per-engine resolvers behind the single resolver a
TargetRoutertakes. This adds both, behind the existingengine.Engineandinventory.Resolverinterfaces, so no engine-specific package is pulled into core.What it does
Engine registry — supply an engine for a database type without a built-in implementation:
tern.EngineFactory+LocalConfig.EngineFactories:NewLocalClientbuilds the engine for a non-built-in type from a registered factory, failing closed when such a type has no factory, the factory errors, or it returns nil. Built-inmysql/vitessare unchanged.api.Service.RegisterEngine(databaseType, factory): the embedder hook, called during setup; registered factories flow into the local clients the service builds.Type-routing resolver — compose multiple engines behind one resolver:
inventory.TypeRoutingResolverroutes a request to the resolver registered for its database type, failing closed when the request carries no type or none is registered.How it moves toward the northstar
Completes the embedder extension surface for the engine-agnostic data plane: an embedding service can run any mix of engines — including ones this build does not ship — behind
engine.Engineandinventory.Resolver, with engine-specific code supplied by the embedder rather than the core.PR drafted by Claude Code (Opus 4.8).