Define a versioned capability schema and a discovery flow where:
- Adapters expose a capability map and a
profileHashatconnect() - Compile profiles (target-specific lowerers, e.g.
sql/pg) consume capabilities to choose deterministic lowering strategies - Lanes and plugins branch on capabilities, never on target strings
- Plans compiled under capability profile X are recompiled when the active
profileHashchanges
Adapters do not lower Plans to SQL. Lowering is the responsibility of compile profiles.
- Keep dialect logic deterministic and testable with golden SQL in compile profiles
- Allow one runtime to execute across heterogeneous deployments by branching on declared capabilities rather than hardcoding "postgres" or "mysql"
- Make fallbacks explicit when a feature is unavailable and avoid silent differences across environments
- Enable reproducible builds by hashing the negotiated capability set per ADR 004
Capabilities are a JSON-serializable object with stable key ordering:
{
"schemaVersion": "1",
"target": { "family": "sql", "profile": "pg", "version": "15" },
"sql": {
"placeholderStyle": "$n",
"cte": true,
"lateral": true,
"window": true,
"jsonAgg": true,
"returning": "all" // "none" | "insert-only" | "all"
},
"ddl": {
"transactional": true,
"concurrentIndex": true,
"partitioning": "native", // "none" | "emulated" | "native"
"renameColumn": true
},
"types": {
"uuid": "native", // "emulated" | "native"
"jsonb": true,
"timestamptz": { "timezone": "UTC-preserving" }
},
"limits": {
"maxParams": 32767,
"identifierMaxLen": 63,
"identifierCase": "preserve"
},
"explain": {
"format": "text", // "text" | "json"
"costs": true,
"buffers": true
},
"tx": {
"savepoints": true,
"advisoryLocks": "session" // "none" | "transaction" | "session"
},
"vendor": {
"pg": { "extensions": { "postgis": false, "vector": false } }
}
}- Reserved top-level segments:
target,sql,ddl,types,limits,explain,tx,vendor,schemaVersion vendor.*is freeform for adapter-specific data- Values are booleans, enums, strings, numbers, or small structured objects
- No functions or code references
profileHash = sha256(canonicalize(capabilities) + compileProfileId)- Used for reproducibility, caching, and change detection per ADR 004
- Changes whenever capabilities or the compile profile implementation changes in a way that affects lowering
-
Compile-time defaults
- Lanes and tools may compile offline using a reference profile shipped with the compile profile (e.g.
pg15-baseline) - This produces SQL tentatively and caches by
(planId, referenceProfileHash)
- Lanes and tools may compile offline using a reference profile shipped with the compile profile (e.g.
-
Connect
adapter.connect()returns{ capabilities, profileHash }- Runtime stores active capabilities and emits
onCapabilitiesNegotiatedfor plugins
-
Before compile
- If a Plan was compiled under a different
profileHash, recompile with the active capabilities - Compile profiles choose the lowest common strategy that satisfies the Plan and capabilities
- If a required feature is missing and no fallback exists, fail with a stable error
- If a Plan was compiled under a different
-
Execute
- Adapter executes
{ sql, params }and maps errors to runtime codes - Optional lightweight probes may validate a small subset of sensitive capabilities but are not required
- Adapter executes
- Lanes must not branch on target strings
- They may query
runtime.getCapabilities()and guard behavior with explicit keys - Plugins can attach rule context using capability checks, e.g.
if (!capabilities.sql.jsonAgg) rule('no-nested-array-projection').warn(...)
- Unknown schema version: Warn and treat unknown keys as opaque, but do not assume capabilities exist
- Missing critical key: Assume the most conservative default, e.g.
ddl.transactional: false, and allow compile profile to reject if it cannot provide a safe lowering - Runtime mismatch: If the connection rotates to a different
profileHashmid-process, the runtime must invalidate compiled outputs and recompile - Degraded capabilities: Compile profile may choose a fallback lowering or return a deterministic error with a suggested capability requirement
schemaVersionis semver-like but single integer for v1- New optional keys may be added without bumping the major version
- Changing meaning of existing keys requires a major bump
- Compile profiles declare supported
schemaVersionranges
{
"schemaVersion": "1",
"target": { "family": "sql", "profile": "pg", "version": "15" },
"sql": { "placeholderStyle": "$n", "cte": true, "lateral": true, "window": true, "jsonAgg": true, "returning": "all" },
"ddl": { "transactional": true, "concurrentIndex": true, "partitioning": "native", "renameColumn": true },
"types": { "uuid": "native", "jsonb": true, "timestamptz": { "timezone": "UTC-preserving" } },
"limits": { "maxParams": 32767, "identifierMaxLen": 63, "identifierCase": "preserve" },
"explain": { "format": "text", "costs": true, "buffers": true },
"tx": { "savepoints": true, "advisoryLocks": "session" },
"vendor": { "pg": { "extensions": { "postgis": false, "vector": false } } }
}{
"schemaVersion": "1",
"target": { "family": "sql", "profile": "sqlite", "version": "3.45" },
"sql": { "placeholderStyle": "?", "cte": true, "lateral": false, "window": true, "jsonAgg": false, "returning": "none" },
"ddl": { "transactional": true, "concurrentIndex": false, "partitioning": "none", "renameColumn": false },
"types": { "uuid": "emulated", "jsonb": false, "timestamptz": { "timezone": "local-converted" } },
"limits": { "maxParams": 999, "identifierMaxLen": 63, "identifierCase": "preserve" },
"explain": { "format": "text", "costs": true, "buffers": false },
"tx": { "savepoints": true, "advisoryLocks": "none" }
}- Provide accurate capabilities at
connect()and a stableprofileHash - Update capabilities if the effective environment changes and notify runtime
- Map database errors to stable runtime error codes per ADR 027
- Do not embed lowering logic or transform ASTs
- Deterministically lower
(AST, capabilities)to{ sql, params } - Prefer conservative fallbacks when possible
- Emit stable diagnostics when a Plan cannot be lowered under the active capabilities
- Ship reference capability profiles for offline compilation and testing
- Participate in
profileHashcreation per ADR 004
- Capability maps must not leak sensitive configuration like passwords or hostnames
- Vendor sections should avoid revealing internal infrastructure details and stick to feature availability or extension flags
- Should we allow a small set of runtime probes behind a feature flag to verify high-risk claims like
ddl.transactional? - Do we need a standardized way to express performance characteristics in capabilities, e.g.
limits.maxExplainCost, or should that remain policy in budgets?
- Deterministic compilation tied to explicit features
- Better portability and fewer target-string branches in code
- Clear error paths when a feature is unavailable
- More upfront work to define and maintain the capability map
- Adapters must keep capability reporting accurate across server versions and configuration
- Hardcode target strings and scatter conditionals: Rejected as brittle and untestable across environments
- Let adapters perform lowering: Rejected because it breaks determinism, complicates testing, and conflates execution with compilation