ADR-036: Public surface stability policy¶
Status¶
Accepted — the stability policy is normative (#369, roadmap decision D6).
Context¶
Awa is consumed through distinct surfaces — the Rust API, the Python API, the SQL producer
contract, HTTP admin API, callback receiver contract, metric names/attributes, and the CLI — and
0.6's beta-series churn (QueueFanout →
PartitionedQueue) showed the cost of having no written boundary: consumers cannot tell a
supported surface from an internal one, and maintainers cannot tell a breaking change from a
refactor. Polyglot producers (#342) and API
consumers cannot exist without a written contract. Accepted designs such as ADR-042's SQL worker
finalization contract join this map only when implementation, public documentation and the surface
table, conformance evidence, and the changelog transition ship together; an upgrade guide is also
required when a new surface replaces an older covered entry point.
Decision¶
Publish and maintain the stability policy as the single normative statement of what is stable:
- A surface-by-surface map: what each surface promises, what is explicitly internal. Anything unlisted is internal and may change in any release without notice.
- Release-type rules while pre-1.0: patches fix bugs only (no new migrations except to repair a defective one); minors (0.x → 0.x+1) play the semver-major role and may break listed surfaces only with a changelog entry, an upgrade path, and — where feasible — a deprecation period; pre-release tags of one line promise nothing to each other.
- A deprecation policy: announce in N, warn at runtime in N+1 where possible, remove in N+2; security fixes may compress the schedule explicitly.
- A binary/schema skew statement, asserted nightly by the compat matrix
(
scripts/compat-matrix.sh, #367) against pinned release artifacts.
Changes to the policy are made by amending the document in an ordinary reviewed PR; release notes list breaking changes against its surface list. If a change breaks something unlisted and that surprises users repeatedly, the fix is to amend the document, not to argue.
Consequences¶
Positive
- Consumers get an explicit contract per surface; maintainers get an explicit licence to change everything else.
- Enforcement has concrete hooks:
cargo-semver-checksfor the Rust rows (#402), the compat matrix for skew,--jsonschema tests for the CLI, and the release checklist in the #383 tracker.
Negative
- The document must move with the code: every PR that adds a
--jsoncommand, a probe field, or a public type either updates it or implicitly leaves that surface internal. - Promising the HTTP response types constrains handler refactors until the response-type module (#403) fully separates them from the DAL.
Alternatives considered¶
- Semver alone — says that something broke, not which surface was ever promised; pre-1.0 semver carries no information for consumers.
- A stability attribute per item (Rust
#[stable]-style annotations) — heavier tooling than a surface map justifies, and it cannot describe SQL, HTTP, metric, or CLI surfaces at all. - Per-crate CHANGELOG discipline without a map — documents changes without defining the boundary; the boundary is the thing consumers need.
Relationship to other ADRs¶
Governs the ADR-016 adapter contract, the #342 SQL producer contract, and ADR-042 only after its implementation, public documentation/table row, conformance evidence, and changelog transition ship together (plus an upgrade guide when replacing an older surface); constrains every future ADR that touches a listed surface; the skew row is enforced by the ADR-037 migrate gate plus the compat matrix.