ADR-0053: WIT Interface Versioning
Date: 2026-08-07
Status: Accepted; implemented
Amends: none
Related: ADR-0014, ADR-0031, ADR-0049, ADR-0050
Origin: camel-wit quality audit, WIT-006 / DP-2
Context
camel-wit defines the component-model ABI between rust-camel and WASM
guests. Its four WIT files currently declare an unversioned
package camel:plugin;. The same unresolved WIT-006 note appears in those
files and in src/lib.rs. rg -n 'TODO\(WIT-006\)' crates/camel-wit verifies
five sites.
Compiled guests depend on package, interface, world, type, and function
identities. A Rust crate release can change without changing that ABI. A WIT
shape change can also break an existing guest even when the Rust API remains
compatible. The Rust crate version and ADR-0049's #[non_exhaustive] policy
therefore cannot express WIT compatibility.
Adding a version after stable guests exist changes package identity and forces a migration without a prior compatibility contract. WIT versioning is thus a v1.0 freeze decision, not post-v1.0 documentation work.
Decision
The camel:plugin WIT package uses an independent package-level SemVer.
- One version covers every interface and world in the package. We do not
version
plugin,bean,authorization-policy, orsourceseparately. - The rust-camel v1.0 release establishes
camel:plugin@1.0.0. Pre-v1 unversioned packages have no compatibility guarantee. - The WIT version does not follow the Rust workspace version. A Rust release that does not change the WIT contract keeps the existing WIT version.
- A change to an existing function, record, variant, enum, resource, import, or export is breaking unless the supported component toolchain proves it compatible in both host and guest directions. Breaking changes increment the WIT major version.
- A proven compatible contract addition increments the minor version. Documentation-only corrections increment the patch version only when a WIT package release needs a distinct identity.
@sinceannotations record the minor version that introduced an element when the supported toolchain can validate them. They supplement the package version and do not replace it.- The host links only package majors that it explicitly supports. It must not silently reinterpret a guest from another major. Supporting two majors requires separate bindings and an explicit migration period.
- Canonical WIT files, generated host bindings, shipped guest examples, and
compatibility tests change in one code change.
rc-aaxetracks the initial1.0.0application.rc-osj0tracks removal of the host's duplicate WIT source.
Consequences
- Package identity detects incompatible guest and host contracts during linking instead of allowing ambiguous runtime behavior.
- WIT evolution can remain stable across unrelated Rust crate releases.
- The package-wide version keeps shared
typesandhostinterfaces coherent across all worlds. - A post-v1 breaking ABI change requires a new package major and host bindings. This cost is deliberate because silently replacing the ABI would break compiled guests.
- The initial implementation changes package identities in canonical files, host bindings, copied files, and examples. It must land before the v1.0 freeze.
Options considered
Defer versioning until after v1.0
Rejected. Adding the first package version after stable guests exist is itself a package-identity break. Deferral would freeze ambiguity into the v1 contract.
Follow every Rust crate version
Rejected. Most Rust releases do not change the WIT ABI. Lockstep versions would signal false incompatibility and couple guest tooling to unrelated Rust work.
Version each world independently
Rejected. The worlds share package-level types and host interfaces.
Independent versions would either duplicate those interfaces or create a
compatibility matrix without a present use case.
Use one independent package version
Accepted. It matches the actual compatibility boundary and permits all worlds to evolve as one contract while remaining independent from Rust releases.
Why this is not an amendment
ADR-0014 governs runtime limits and configuration. ADR-0031 defines source world lifecycle. ADR-0049 governs Rust enum evolution. ADR-0050 governs sandbox capabilities. None defines ABI identity or compatibility across WIT releases. This decision is orthogonal and applies to all WASM worlds, so it needs its own ADR.
Self-grill record
Questions generated:
- [glossary] Does “WIT package version” overlap Rust crate SemVer or the WASM sandbox capability posture?
- [sharpen] Is the compatibility unit one package, one interface, or one world?
- [scenario] What happens if
wasm-exchangegains a field after v1.0 while the package remainscamel:plugin@1.x? - [cross-ref] Can an existing ADR own this decision, or can versioning wait until after the v1.0 release?
Answers:
- [glossary] It is separate from both. The workspace crate version is
0.26.0(Cargo.toml:54), while WIT files are unversioned (crates/camel-wit/wit/camel-plugin.wit:1). ADR-0050 controls granted host capabilities, not package compatibility. - [sharpen] The package is the compatibility unit.
plugin,bean, andsourcesharecamel:plugininterfaces and types (camel-plugin.wit:8-94,camel-bean.wit:12-18,camel-source.wit:9-82). Per-world versions would split shared types. - [scenario] Existing guests compiled against the old record shape can fail to link or lower/lift values correctly. Under this decision, that shape change defaults to a major bump unless compatibility tooling proves both directions safe. It cannot pass as an undocumented additive Rust change.
- [cross-ref] No existing ADR governs WIT evolution. ADR-0049 explicitly
covers Rust contract enums, and ADR-0050 covers the host capability surface.
Deferral fails because adding
@1.0.0later changes the identity consumed by the four Wasmtimebindgen!sites incamel-component-wasm/src/.
Outcome: confirm as new ADR. The decision is hard to reverse after guests compile, surprising without the Rust/WIT version distinction, and resolves a real trade-off between lockstep, per-world, and package-wide versioning. Self-grill mode: self-grill-proposals skill