feat: world-class SDK — typed domains, four real roles, compat declaration, conformance harnesses#10
Merged
Merged
Conversation
Replace three bare-`str` value domains with `str`-mixed enums (the zero-dep pre-3.11 StrEnum recipe; we avoid `enum.StrEnum` 3.11+ and `backports.strenum` to keep the SDK stdlib-only). Members equal and serialise as their value, and an explicit `__str__` gives StrEnum semantics on 3.10. Fixes the severity footgun: the reference `Validator.apply` previously bucketed `counts[severity]` by raw string and tallied `failed` only from the literal "error"/"critical" keys, so a typo'd severity (e.g. "errror") landed in a phantom bucket and silently escaped the failure tally. `Severity.coerce` now normalises aliases and fails safe — an unrecognised severity counts as ERROR and is surfaced in `ExecutionResult.warnings`. `Finding` normalises severity on construction. Regression test pins the behaviour. Borrowed: dbt-core closed-vocabulary + loud-coercion posture; CPython enum HOWTO StrEnum recipe.
…nd-gate) The CLI's compat check reads MIN_CLI_VERSION/MAX_CLI_VERSION off the SDK module and otherwise falls back to the renamed-away `fluid_provider_sdk` (dead import), so the handshake never fired. Give the live SDK the proper declaration surface: - version.py: SDK_PROTOCOL_VERSION (interface generation), MIN/MAX_CLI_VERSION, and cli_requirement() -> PEP 440 specifier string. - PluginMetadata: carry sdk_protocol_version + requires_cli (inherits the SDK default when unset), surfaced in to_dict() and the default get_plugin_info(). - Export the surface from the package root; pin it in the strict public-API test. The SDK only declares (stays stdlib-only); the CLI does the SpecifierSet gate. Borrowed: dbt require-dbt-version declare-and-gate model.
…esses The README/docs promised four roles and "15+ free tests for all 4 roles", but only CustomScaffold and Validator existed and InfraProvider/CatalogAdapter were doc fiction; the advertised ValidatorTestHarness / InfraProviderTestHarness / CatalogAdapterTestHarness raised ImportError. Close the gap at the root: - New role ABCs InfraProvider (role="provider") and CatalogAdapter (role="catalog"), each with an action builder (provision_action / catalog_entry_action) and apply LEFT ABSTRACT so a provider that forgets to implement it fails loudly instead of silently no-op'ing a deployment. - Typed PluginCapabilities + BasePlugin.capabilities() with per-role defaults (replaces the prose-only "capability defaults" the docstrings claimed). - Ship the three missing role harnesses; raise the universal harness floor: capabilities declared, compat declared, plan JSON-serialisable, and an opt-in apply-reflects-plan check (no silent action drops). - Zero-dep golden-file snapshot helper (assert_plan_matches_snapshot) mirroring syrupy's UX (fail-on-missing, FLUID_SNAPSHOT_UPDATE=1) — dbt-grade plan pinning for every role, no dependency. Borrowed: dbt-tests-adapter tiered base-class pattern; syrupy snapshot UX. 136 tests pass (toy plugins drive the full per-role conformance suite).
fas89
added a commit
that referenced
this pull request
Jun 27, 2026
…ns, conformance, compat) (#16) Finalizes the CHANGELOG [Unreleased] section as [0.10.0] and bumps both version constants (pyproject + fluid_sdk.version.SDK_VERSION) 0.9.1 -> 0.10.0. Ships the world-class SDK series merged since v0.9.1 (no PyPI release had carried it): #10 typed domains + four real role ABCs (InfraProvider, CatalogAdapter) + PluginCapabilities + SDK<->CLI compat declaration + conformance harnesses; #11-#15 type/doc-honesty cascade and docs-honesty CI gate; #9 actions/checkout bump. SDK_PROTOCOL_VERSION stays 1 (additive, non-breaking). MIN_CLI_VERSION unchanged (0.7.0).
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.
What & why
Part 1 of the "world-class SDK + plugin" push. Closes the SDK-side gaps from a
multi-agent audit: typed value domains (with a real fail-safe fix), the four-role
promise made true, a plugin↔CLI compatibility declaration, the three
previously-fictional role harnesses, and zero-dep snapshot testing.
Raises api-design and conformance toward 10/10; lands the SDK half of the
version-negotiation fix (CLI half is a follow-up PR).
Changes (each its own green commit)
feat(domains)—Severity/ActionStatus/Phaseas zero-depstr-enums(avoids
enum.StrEnum3.11+ andbackports.strenumto keep stdlib-only).Fixes the severity footgun:
Validator.applybucketed by raw string andtallied failures from literal keys, so a typo'd severity (
"errror") silentlyescaped the failure tally.
Severity.coercenow fails safe (unknown → ERROR,surfaced in
warnings). Regression test pins it.feat(compat)—SDK_PROTOCOL_VERSION,MIN/MAX_CLI_VERSION,cli_requirement(), andPluginMetadata.{sdk_protocol_version,requires_cli}.The CLI's compat check read these off the (renamed-away)
fluid_provider_sdk;the live SDK now declares them. SDK declares, CLI gates (dbt model).
feat(roles,testing)—InfraProvider+CatalogAdapterare now realABCs (were doc fiction);
applyleft abstract so a provider that forgets itfails loudly, not silently.
PluginCapabilities+capabilities(). Ship the3 advertised-but-missing role harnesses; raise the universal floor
(capabilities/compat declared, plan JSON-serialisable, apply-reflects-plan).
Zero-dep
assert_plan_matches_snapshot(syrupy UX, no dependency).docs(changelog)— record the above under[Unreleased].Borrow-before-build
pluggy + stevedore (manager lifecycle), dbt
require-dbt-version(declare-and-gate),dbt-tests-adapter (tiered base harnesses), syrupy (snapshot UX), CPython enum
HOWTO (pre-3.11 StrEnum recipe). Divergences (no
backports.strenum, vendored~30-line snapshot) are deliberate to preserve the zero-dependency invariant.
Verification
ruff+blackclean at CI-latest (0.15.20 / 26.5.1).yaml/subprocess/network; the one file-writing helper takes static test-literal
paths (not an untrusted boundary); the real traversal guard in
CustomScaffold.applyis untouched.PYTHONPATH): built wheel +twine checkpass; clean install imports the full new surface; the 3 shipped exampleplugins pass against the wheel (25/29/21); a fresh out-of-tree InfraProvider
plugin passes 23 conformance tests against the installed wheel and is
discovered + loaded via
importlib.metadata(the CLI's mechanism).Zero-dependency invariant preserved (
pip installpulls only stdlib).