Adapter authoring
An adapter is the platform implementation layer for ContractForge. It does not change what a contract means. It decides whether the target platform can preserve that meaning, renders native artifacts and, when supported, executes them with auditable evidence.
Use the Databricks adapter as the reference style. Use AWS and Snowflake as examples of stable non-Databricks surfaces. A new adapter does not need to copy their internal modules, but it must preserve the same public contract boundary.
Mental model
contract YAML
-> core validation and semantic normalization
-> adapter capability planning
-> native artifact rendering
-> optional publish/register/execute
-> evidence and diagnostics
The core owns contract vocabulary. The adapter owns native binding.
| Layer | Owns | Must not do |
|---|---|---|
| Core | Contract schema, normalization, planning primitives, neutral evidence models, source taxonomy and write-mode aliases. | Import platform SDKs or know platform credentials. |
| Adapter | Capabilities, native naming, source execution, writes, governance, deployment, runtime clients and platform evidence. | Silently downgrade semantics or add platform meaning to portable contract fields. |
Minimal public interface
The public adapter shape is intentionally small:
from contractforge_core.adapters import PlatformAdapter, RenderedArtifacts
from contractforge_core.capabilities import PlatformCapabilities
from contractforge_core.planner import ExecutionPlan, PlanningResult, plan_contract
from contractforge_core.semantic import SemanticContract
class MyPlatformAdapter:
name = "my-platform"
def capabilities(self) -> PlatformCapabilities:
...
def plan(self, contract: SemanticContract) -> PlanningResult:
return plan_contract(contract, self.capabilities())
def render(self, plan: ExecutionPlan) -> RenderedArtifacts:
...
Production packages can expose richer adapter-owned APIs such as
deploy_project, execute_plan, apply_access_contract or
stabilization_report. Those APIs should still reduce to capabilities,
planning, rendering, execution and evidence.
Authoring lifecycle
| Step | Output | Acceptance signal |
|---|---|---|
| Declare capability | Conservative PlatformCapabilities. | Unsupported and review-required semantics are explicit. |
| Plan contracts | PlanningResult with warnings and blockers. | Core statuses are preserved: SUPPORTED, SUPPORTED_WITH_WARNINGS, REVIEW_REQUIRED, UNSUPPORTED. |
| Render artifacts | SQL, scripts, job definitions, manifests or review Markdown. | Rendering never implies execution for unsafe plans. |
| Bind runtime | Platform clients, credentials, warehouses, jobs or clusters. | SDKs are imported lazily or injected by callers. |
| Execute safely | Adapter-owned runner. | Runs are idempotent where promised and fail with useful diagnostics. |
| Persist evidence | Queryable run, quality, error, schema, lineage, governance and cost records. | Evidence is redacted and can prove what happened. |
| Document scope | Adapter guide and capability matrix. | Users can see supported, warning, review and unsupported boundaries. |
Required surfaces
| Surface | Required for stable adapter | Notes |
|---|---|---|
| Capability model | Yes | Declare only semantics the adapter can preserve. Similar native features are not enough. |
| Source support catalog | Yes | Publish source family support, native mapping, status and review notes. |
| Write mapping | Yes | Map append, overwrite, upsert, hash_diff_upsert, historical and snapshot reconciliation explicitly. |
| Schema policy | Yes | Preserve strict, additive_only, permissive behavior and type-widening decisions. |
| Quality execution | Yes | Evaluate or render supported checks and persist rule-level evidence. |
| Governance mapping | Platform-dependent | Map annotations, operations and access where native controls exist. |
| Evidence storage | Yes | Stable adapters need queryable evidence, not only log messages. |
| Runtime execution | Expected | Render-only adapters are useful, but they are not mature runtime adapters. |
| CLI/API integration | Expected | Keep core commands generic and adapter commands platform-owned. |
Capability rules
Capabilities are a contract with users. Be conservative.
Do:
- return
SUPPORTEDonly for equivalent behavior; - return
SUPPORTED_WITH_WARNINGSfor non-breaking caveats; - return
REVIEW_REQUIREDwhen a platform design choice is needed; - return
UNSUPPORTEDwhen semantics cannot be preserved; - document why each boundary exists.
Do not:
- treat append as a fallback for
upsert; - treat overwrite as a fallback for snapshot reconciliation;
- treat a time-travel table as complete
historicalsupport unless current-row flags, validity windows and late-arriving behavior are preserved; - execute review-required plans by default;
- persist secrets in metadata or evidence.
Source and connector behavior
Adapters consume the core source taxonomy and bind it to native reads.
| Source family | Adapter obligation |
|---|---|
| Catalog/table/query | Resolve logical refs such as source.ref and {{ table_ref:silver.orders }} to native table names. |
| Files and object storage | Preserve format options, schema handling, credentials and path safety. |
| Incremental files | Map to a platform file-tracking mechanism or return review-required. |
| JDBC | Handle drivers, partitioning, credentials and predicate pushdown without leaking secrets. |
| REST and HTTP | Use bounded, validated fetch behavior or render review/native artifacts. |
| Bounded streams | Implement catch-up replay semantics unless a stronger streaming contract is explicitly documented. |
| Native passthrough | Keep platform-specific connector configuration behind adapter-owned fields and review artifacts. |
Platform-specific connector names should not become portable core input. If the adapter supports native shortcuts, document them under adapter extensions and warn on unknown extension keys.
Write-mode mapping
Public contracts use write-mode aliases. Canonical internal names stay inside the library.
| Contract mode | Adapter must prove |
|---|---|
append | Rows are added without target reconciliation. |
overwrite | The declared target scope is replaced with platform-appropriate atomicity. |
upsert | Stable keys update current state deterministically. |
hash_diff_upsert | Row-hash comparison semantics are preserved. |
historical | History rows, current-row marker, validity windows and late-arriving behavior are preserved. |
snapshot_reconcile_soft_delete | The source is complete before missing keys are marked inactive or deleted. |
custom:<name> | Execution goes through an explicit adapter-registered handler. |
If the adapter cannot preserve a mode, it must block or require review. It must not silently run a weaker write.
Evidence contract
Stable adapters need evidence that can be queried after a run. At minimum, persist or render these concepts:
| Evidence | Minimum requirement |
|---|---|
| Runs | Status, source, target, mode, timestamps, metrics and error summary. |
| Quality | Rule-level result, severity, failed counts and quarantine reference when used. |
| Errors | Redacted error message, platform diagnostic and failed step. |
| Schema changes | Detected drift, applied changes and policy decision. |
| State | Watermarks, locks or idempotency keys needed by supported semantics. |
| Lineage | Source and target references, operation and run correlation. |
| Governance | Applied annotations, grants, policies, masks and row filters. |
| Cost | Platform-native cost signal when available. |
Evidence must be redacted before persistence. If cost attribution or governance evidence is not available, document the limitation instead of fabricating it.
Governance and access
Adapters should map ContractForge governance to native controls when equivalent platform features exist:
| Contract concept | Native examples |
|---|---|
| Table and column descriptions | Catalog comments, metadata APIs, data catalog descriptions. |
| Tags, aliases and sensitivity | Native tags, labels, classifications or policy tags. |
| Grants | SQL grants, IAM, Lake Formation, role bindings. |
| Row filters | Row access policies, data filters or reviewed security policies. |
| Column masks | Masking policies, policy tags or column-level security. |
| Drift policy | Compare declared and current state; require explicit approval for destructive changes. |
When native inheritance or enforcement semantics differ, return
REVIEW_REQUIRED.
Runtime and deployment pattern
Keep deployment separate from contract meaning:
contract bundle
-> render native artifacts
-> publish artifacts when needed
-> register/update platform job
-> execute only when requested
-> record evidence
Examples:
- Databricks renders and deploys Asset Bundles, jobs and Delta-backed evidence.
- AWS publishes artifacts to S3, registers Glue jobs and writes Iceberg evidence.
- Snowflake stages code, runs SQL or hosted procedures and writes audit tables.
The environment contract owns runtime hints such as warehouse, cluster, role, artifact URI, dependency location and evidence location. The ingestion contract continues to own dataset semantics.
Suggested package layout
Keep platform concerns separated:
contractforge_<platform>/
adapter.py
api.py
environment.py
capabilities/
sources/
execution/
schema/
quality/
governance/
evidence/
runtime/
rendering/
diagnostics/
tests/
Avoid one large renderer that owns source parsing, SQL generation, execution, evidence and governance at once. It becomes hard to prove maturity.
Testing matrix
| Test group | Required coverage |
|---|---|
| Core boundary | Adapter imports core; core never imports adapter. |
| Capability planning | Supported, warning, review-required and unsupported plans. |
| Contract parsing | Ingestion, annotations, operations, access and environment. |
| Sources | Every declared supported source family, plus unsupported/review cases. |
| Writes | Every declared write mode and no silent fallback. |
| Schema | Strict, additive, permissive, type widening and blocker paths. |
| Quality | Pass, fail, warn, abort and quarantine behavior. |
| Governance | Annotations, grants, filters, masks and drift policy where supported. |
| Evidence | Run, quality, error, schema, state, lineage and governance records. |
| Runtime | Fake runners first; real-platform smoke tests before stable claims. |
| Redaction | Secrets, tokens, credentials and unsafe URLs never appear in evidence. |
End-to-end maturity means the same contract family can run bronze to gold using only contracts and documented environment bindings, without workaround scripts.
Maturity levels
| Level | Meaning |
|---|---|
| Render-only | Produces review/native artifacts but does not execute. |
| Runnable | Executes simple contracts with adapter-owned runtime code. |
| Validated | Has real end-to-end tests for representative sources, writes, quality and evidence. |
| Stable | Has documented scope, repeated evidence, failure handling, governance boundaries and no silent semantic downgrade. |
| Production-certified | Adds operational runbooks, cost attribution, security review, support policy and upgrade guarantees. |
Stable does not mean every platform feature is implemented. It means the documented surface is explicit, tested and honest.
Documentation checklist
Every stable adapter should publish:
- supported contract sections;
- source connector support;
- write-mode behavior;
- schema and quality behavior;
- annotations, operations and access mapping;
- evidence/control-table mapping;
- runtime prerequisites and credentials;
- deployment flow;
- unsupported semantics;
- review-required semantics;
- real end-to-end evidence.
Anti-patterns
Avoid these:
- adding platform-specific source names to core when an adapter extension is enough;
- accepting unknown extension keys silently;
- executing review-required plans by default;
- making generated review artifacts look executable;
- importing platform SDKs at package import time;
- treating examples as proof of support without evidence tables and tests;
- documenting only happy paths;
- using adapter docs to hide semantic differences.
Acceptance checklist
An adapter is mature enough to call stable when:
- capabilities are conservative and documented;
- every supported write mode has tests and runtime evidence;
- source support is machine-readable or clearly documented;
- unsupported and review-required semantics are visible to users;
- evidence persists run, quality, error, schema and lineage records;
- secrets are redacted;
- deployment does not change contract semantics;
- end-to-end tests use contracts, not workaround code;
- docs show both shared contract reuse and native binding differences.
The exhaustive implementation contract is in Adapter Authoring Guide.