RFC-0006: adapter extension API
Version: 0.1.1 | Status: normative | Phase: impl
1. Summary
[RFC-0006:C-SUMMARY] Summary (Informative)
This RFC specifies an adapter extension API that enables third-party adapters to be registered and used by the typub CLI without modifying core source code.
Scope:
- Defines the
AdapterRegistrarAPI for runtime registration of adapter factories and capabilities. - Specifies capability lookup order when multiple sources provide capabilities.
- Specifies backward compatibility constraints for existing built-in adapters and configurations.
Out of Scope:
- Dynamic library plugin loading (deferred to a future RFC).
- Data-driven adapters via manifest/template (deferred to a future RFC).
Rationale:
The current adapter architecture requires editing multiple core files (Adapter enum, factory array, adapters.toml) to add new adapters. This creates friction for third-party integrations and makes the codebase harder to maintain. A registration-based API decouples adapter creation from core dispatch logic, enabling extension crates to inject adapters at startup.
References:
- RFC-0002 — publish pipeline contract (defines adapter boundary and pipeline stages)
- ADR-0002 — shared types in
typub-core(capability enums)
Since: v0.1.0
2. Specification
[RFC-0006:C-REGISTRAR-API] Adapter Registrar API (Normative)
The AdapterRegistrar struct MUST provide the following methods:
-
register_factory(platform_id: &str, factory: AdapterFactory) -> Result<()>- Registers a factory function that creates an adapter instance for the given platform ID.
- The factory MUST be invoked with the current
ConfigwhenAdapterRegistry::new()is called (eager construction). - If a factory is already registered for the same platform ID, the registration MUST return an error.
- If
platform_idis empty, the registration MUST return an error.
-
register_capability(platform_id: &str, capability: AdapterCapability) -> Result<()>- Registers capability metadata for the given platform ID.
- If a capability is already registered for the same platform ID, the registration MUST return an error.
- If
platform_idis empty, the registration MUST return an error.
The AdapterFactory type alias MUST be defined as:
#![allow(unused)]
fn main() {
pub type AdapterFactory = fn(&Config) -> Result<Box<dyn PlatformAdapter>>;
}
The AdapterCapability struct MUST use owned strings (String or Cow<'static, str>) for the id, name, short_code, and notes fields.
Construction semantics: Adapter construction is eager — all registered factories are invoked during AdapterRegistry::new(). If any factory fails, the entire registry construction MUST fail with an error that identifies the failing platform ID. Lazy construction is out of scope for this RFC.
Rationale:
Eager construction matches existing behavior and ensures all configuration errors are surfaced at startup. Rejecting empty platform IDs prevents silent misregistration. Using Cow<'static, str> preserves zero-cost access for built-in capabilities while enabling owned strings for external adapters.
Since: v0.1.0
[RFC-0006:C-CAPABILITY-LOOKUP] Capability Lookup Order (Normative)
The adapter_capability(platform_id: &str) function in the main runtime path MUST resolve capabilities in the following order:
- Built-in API adapter capabilities (from
BUILTIN_ADAPTERSstatic table, matched byidfield) - Copy-paste profile capabilities (from
copypaste::find_profile()matched by platform ID)
The platform_id parameter is the platform identifier (for example, ghost, notion, wechat).
The first match MUST be returned. If no match is found in any source, the function MUST return None.
The lookup MUST NOT modify global state.
Implementations MAY provide additional extension capability sources in alternate bootstrap paths, but those paths MUST preserve deterministic precedence and MUST NOT change built-in capability semantics for unchanged platform IDs.
Rationale: This reflects the current runtime architecture, where first-party capabilities are resolved from static capability tables and copy-paste profiles. It keeps capability lookup deterministic while leaving room for future extension bootstrap modes.
Since: v0.1.0
[RFC-0006:C-EXTERNAL-VARIANT] External Adapter Variant (Normative)
The adapter registry MUST store adapter instances as trait objects (Box<dyn PlatformAdapter>) keyed by platform ID.
A dedicated Adapter enum with an External variant is NOT required by this RFC.
Implementations MAY introduce wrapper enums internally, but externally visible behavior MUST remain:
- Factory-created adapters are registered under stable platform IDs.
- All
PlatformAdaptermethods are invoked through the trait boundary. - Adapter selection and execution semantics remain equivalent to direct trait-object dispatch.
Rationale: The current implementation already uses trait-object dispatch directly in the registry. Requiring an additional wrapper enum would add indirection without changing behavior.
Since: v0.1.0
[RFC-0006:C-BUILTIN-MIGRATION] Built-in Adapter Migration (Normative)
Built-in adapters MUST provide a stable creation and capability surface suitable for centralized registry construction.
At minimum, each first-party adapter crate MUST expose:
- a factory entrypoint (
create(config) -> Box<dyn PlatformAdapter>or equivalent), and - a canonical capability declaration (
CAPABILITYor equivalent).
The main runtime registry MAY be constructed via:
- a centralized built-in factory table, or
- a registrar-based assembly path, provided externally observable behavior is equivalent.
Built-in adapter registration semantics MUST preserve:
- API adapters are active only when their config section is present and
enabled = true. - Copy-paste profiles are active unless explicitly disabled.
- User-defined
type = "manual"platforms are mapped to copy-paste adapters.
If multiple construction paths exist, tests MUST verify they are behaviorally equivalent for first-party adapters.
Rationale: This codifies current behavior while keeping room for future registrar-first consolidation without forcing an intermediate architecture step.
Since: v0.1.0
[RFC-0006:C-BACKWARD-COMPAT] Backward Compatibility (Normative)
The adapter extension API MUST NOT introduce breaking changes to:
-
Configuration semantics: Existing
typub.tomlplatform configurations MUST continue to work without modification. -
CLI behavior: Existing CLI commands (
publish,build,preview) MUST produce semantically equivalent results for built-in platforms.Equivalence contract: Two outputs are semantically equivalent if they match on:
- Exit code (success vs failure).
- Published content body (ignoring whitespace normalization).
- Asset URLs or references (same assets resolved).
- Status file updates (same platform status recorded).
Differences in log output, formatting, and non-functional metadata (timestamps, tool version) are permitted.
-
PlatformAdapter trait: Required trait methods MUST NOT be removed or have their signatures changed in incompatible ways. New methods with default implementations MAY be added.
-
Pipeline contract: The publish pipeline stages defined in RFC-0002 MUST NOT be modified.
-
Extension isolation: The presence of an extension adapter MUST NOT alter the output for platforms that do not use that extension. An extension that registers platform ID “foo” MUST NOT affect the behavior of platform ID “bar”.
Error messages for unknown platforms MUST include:
- The platform ID that was not found.
- Guidance that registration may be missing (e.g., “not registered” or “may need to be registered”).
The exact wording of error messages is not normative.
Rationale: Users rely on stable configuration and CLI behavior. Breaking changes would force migration effort and risk regressions in production workflows. The equivalence contract provides a testable definition for parity verification.
Since: v0.1.0
[RFC-0006:C-CLI-BOOTSTRAP] CLI Bootstrap Hook (Normative)
The CLI MUST initialize adapter availability before command dispatch.
Bootstrap MUST:
- Construct the first-party adapter registry from configured platforms.
- Ensure platform capability lookup is available for selected targets.
- Pass the resulting registry into pipeline execution.
Implementations MAY support additional extension bootstrap hooks (for example registrar-based extension crates), but such hooks are OPTIONAL in this RFC revision.
When extensions are enabled, bootstrap MUST preserve first-party behavior for unchanged platform IDs.
The CLI MUST operate correctly when only first-party adapters are present.
Rationale: This reflects the current production path and keeps extension wiring as an optional, non-blocking capability.
Since: v0.1.0
[RFC-0006:C-REQUIRED-TESTS] Required Tests (Normative)
The implementation MUST include the following test categories:
Negative tests:
- Duplicate registration:
register_factoryandregister_capabilityMUST return an error when the same platform ID is registered twice. - Empty ID:
register_factoryandregister_capabilityMUST return an error whenplatform_idis empty. - Capability precedence: Tests MUST verify that runtime-registered capabilities take precedence over built-in capabilities for the same platform ID.
Parity tests: 4. For each built-in adapter, a test MUST verify that the registrar-based path produces semantically equivalent output to the legacy path (if the legacy path still exists during transition). Equivalence is defined per RFC-0006:C-BACKWARD-COMPAT.
Isolation tests:
5. Tests MUST verify that registering an extension adapter for platform “foo” does not alter the output of publish, build, or preview for platform “bar”.
Override warning tests:
6. Tests MUST verify that overriding a built-in capability emits a warning unless suppress_capability_override_warning = true is set.
Rationale: Explicit test requirements ensure the implementation is verifiable and reduce the risk of regressions or undefined behavior at edge cases.
Since: v0.1.0
Changelog
v0.1.1 (2026-02-22)
Align adapter extension clauses with current runtime architecture
Added
- Replace External-enum and mandatory registrar bootstrap wording with trait-object registry semantics
- Update capability lookup and built-in migration clauses to current implementation model
v0.1.0 (2026-02-14)
Initial draft