> ## Documentation Index
> Fetch the complete documentation index at: https://docs.visiqlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK versioning & compatibility

> The VisIQ SDK versioning posture — pre-1.0 today (no wire-compatibility guarantee yet), the two engineering invariants we hold even now, and the compatibility window that takes effect at v1.0.

This page describes how the `@visiq/harness` SDK and its sibling packages
evolve: what a version number promises today, the invariants that hold
regardless of version, and the compatibility commitment that takes effect at
v1.0.

<Note>
  This is about the **SDK** — the client library you install (`@visiq/harness`,
  `visiq`, and the other language bindings). The **OEM Partner API** has its own,
  separate versioning contract with a dated version header and a 12-month
  deprecation window — see [Versioning & deprecation](/partners/oem-versioning).
</Note>

***

## Pre-1.0: no wire-compatibility guarantee yet

**The VisIQ SDK is pre-1.0 (0.x): there is no wire-compatibility guarantee
yet. Breaking changes may ship in any minor release until v1.0 GA.**

This is exactly what a `0.x` version already means under
[Semantic Versioning 2.0](https://semver.org/#spec-item-4): "anything MAY
change at any time; the public API SHOULD NOT be considered stable." A minor
bump (`0.2.0` → `0.3.0`) is allowed to remove or rename a field, change a
default, or tighten a shape — and our published changelog does exactly that
(for example, `0.2.0` made `fleet.heartbeat()` require an argument that earlier
releases did not).

We are treating the pre-launch window as deliberate design time: **the point of
staying on 0.x is to get the wire contract right before we commit to it.** Once
a construct is in a v1.0 API it is expensive to change; pinning that contract
prematurely would trade a few months of convenience for years of carrying a
shape we later regret. So the posture is intentional, not an oversight.

**What this means for you today**

* **Pin an exact version** and upgrade deliberately. Don't float a range across
  a minor boundary in an unattended pipeline.
* **Read the changelog before upgrading.** Every breaking change is called out
  under a `Breaking` / `Removed` heading in that package's changelog.
* Expect the surface to keep moving until the v1.0 line below is in effect.

***

## Two invariants we hold even at 0.x

"Pre-1.0" governs the *shape* of the SDK surface — the types, fields, and
function signatures you compile against. Two properties are **not** on the
table even while we are pre-1.0, because a governance product cannot be casual
about them. Both are enforced by the release process and proven continuously,
not left to reviewer diligence.

### 1. The wire contract fails closed, and the SDK ships first

The control plane and the SDK talk over a rule **bundle** — a document of
outcome verbs (`permit`, `deny`, `approval_required`, `mask`, `redact`,
`escalate`), fields, and multi-valued `operations[]` facets (`retrieval`,
`action`, `delegation`). Two rules govern that document forever:

* **Additive-only, SDK ships first.** The control plane never emits a bundle
  construct — a new outcome verb, a new field, or a new `operations[]` facet —
  that the reading SDK cannot already enforce. The SDK that understands a
  construct is published *before* the plane starts emitting it. This is the
  **tolerant-reader** principle applied in the safe direction: readers tolerate
  what they don't need, but the writer never gets ahead of the readers on
  anything load-bearing.
* **Fail closed on the unrecognized.** Every SDK treats a construct it does not
  recognize as **MUST-UNDERSTAND**: it does not silently ignore it and proceed.
  A feature-flag SDK can afford to skip an unknown flag; a governance SDK cannot
  skip an unknown *restriction*. So an unrecognized outcome or an unparseable
  bundle resolves to the fail-closed outcome for a confirmed-enforcing agent —
  deny the action, suppress the document — never fail open. This is the same
  fail-closed ladder the runtime already follows when a bundle is missing or a
  mask fails (see [Local evaluation](/reference#local-evaluation)).

Together these mean a version skew between plane and SDK can degrade *coverage*
(an older SDK may not know about a brand-new rule facet) but can never degrade
*safety* (it will never wave through something it didn't understand). This
invariant is guarded by a forward-compatibility corpus — bundles carrying
not-yet-known constructs, which every SDK must resolve to the fail-closed
outcome — that runs on every release.

### 2. Every shipped plugin tracks the current stable host

The CLI plugins — [Claude Code](/quickstart/claude-code) and
[OpenClaw](/quickstart/openclaw) — are the one place our governance rides
inside another program's process. For each, the invariant is: the shipped
plugin both **registers with** and **actually enforces against** the current
stable release of its host. It is not enough to load — a plugin that installs
but no longer intercepts the host's tool calls is worse than none, because it
looks governed.

We verify this **daily against the real host binary**, not a mock: a canary
installs the current stable Claude Code and OpenClaw, drives a tool call that a
rule denies, and asserts the call was actually blocked and recorded. A host
release that moves an interception point turns the canary red before it can
reach a user.

***

## At v1.0: the compatibility window

<Warning>
  **Not yet in effect.** The commitment below takes effect at the v1.0 GA
  release. Until then the pre-1.0 posture above governs.
</Warning>

v1.0 is where the wire contract **freezes** and the SDK enters a supported
compatibility window. From GA:

* **We support any SDK released in the last 12 months.** A control plane will
  interoperate with every SDK version published within the trailing twelve
  months, across **all** the SDKs we distribute — TypeScript (`@visiq/harness`
  on npm), Python (`visiq` on PyPI), Ruby (`visiq` on RubyGems), Java
  (`com.visiqlabs:visiq-sdk` on Maven Central), and the Rust and Go bindings
  distributed via git/module. You get a full year to adopt a new release, not a
  flag day.
* **Breaking changes move to major versions.** After 1.0, a change that removes
  or renames a surface, changes a default, or tightens a shape ships in the next
  **major** (`2.0.0`), with the prior major supported through the window above.
  Minors and patches stay additive and backward-compatible.

The model mirrors established practice rather than inventing one:

* **[SemVer 2.0](https://semver.org/)** — the pre-1.0 / post-1.0 split and the
  major-for-breaking rule are SemVer verbatim.
* **[Kubernetes version-skew policy](https://kubernetes.io/releases/version-skew-policy/)**
  — a bounded, published window in which older and newer components are
  guaranteed to interoperate, so operators upgrade on their own schedule.
* **Tolerant-reader / schema BACKWARD & FORWARD compatibility** — the
  additive-only, fail-closed-on-unknown discipline above is the standard
  contract-evolution posture from schema-registry ecosystems (Avro/Protobuf),
  applied in the direction a governance system requires.

Until v1.0, treat every minor as potentially breaking, pin exact versions, and
read the changelog. The two invariants above hold the whole way there.
