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

# Versioning (tai42_contract.versioning)

> The VersionedStore primitive behind presets and policies.

The generic versioned-document store contract.

A `kind`-discriminated, body-opaque persistence primitive: append-only version
rows over an opaque JSONB `body`, an active-version pointer, and rollback. The
store knows NOTHING about presets/policies/agents — those are typed VIEWS layered
on top. Identity is `(kind, name)` throughout; every write is one transaction.

This package holds the `VersionedStore` Protocol plus its record models
(`DocumentRecord`, `DocumentVersion`) and typed errors. It is
reached from the assembled facade as `app.versioning.store` (see
`AppVersioning`).

## DocumentExistsError

`tai42_contract.versioning.errors.DocumentExistsError`

```python theme={null}
class DocumentExistsError(DocumentStoreError)
```

A live document already exists for `(kind, name)` (create would collide).

## DocumentNotFoundError

`tai42_contract.versioning.errors.DocumentNotFoundError`

```python theme={null}
class DocumentNotFoundError(DocumentStoreError)
```

No active document for `(kind, name)`.

## DocumentRecord

`tai42_contract.versioning.models.DocumentRecord`

```python theme={null}
class DocumentRecord(BaseModel)
```

A versioned document's live record: its identity plus the active pointer.

`active_version` names the version currently served for `(kind, name)`;
rollback re-points it without copying data. `is_active` is the soft-delete
flag — a soft-deleted document keeps its version history (audit) but drops out
of `list`/`get`. `created_at` is an ISO-8601 timestamp string.

**Attributes**

| Attribute        | Type   |
| ---------------- | ------ |
| `kind`           | `str`  |
| `name`           | `str`  |
| `active_version` | `int`  |
| `is_active`      | `bool` |
| `created_at`     | `str`  |

## DocumentStoreError

`tai42_contract.versioning.errors.DocumentStoreError`

```python theme={null}
class DocumentStoreError(Exception)
```

Base for versioned-document store failures, carrying `(kind, name)`.

**Attributes**

| Attribute | Type |
| --------- | ---- |
| `kind`    | —    |
| `name`    | —    |

## DocumentVersion

`tai42_contract.versioning.models.DocumentVersion`

```python theme={null}
class DocumentVersion(BaseModel)
```

One immutable version row: the opaque `body` plus its version number.

A version is append-only — once written, its `version`, `body`, `tags`,
and `created_at` never change. `tags` is a generic per-version label for
grouping versions (release/grouping labels); it is kind-agnostic and carries
no product meaning (distinct from any categorization a `kind`'s body may
hold). `is_current` is a read-time projection of the owning record's
`active_version` — the store sets it `True` on the active version when it
lists/returns versions; it is not stored on the row. `created_at` is an
ISO-8601 timestamp string.

**Attributes**

| Attribute      | Type             |
| -------------- | ---------------- |
| `model_config` | —                |
| `version`      | `int`            |
| `body`         | `dict[str, Any]` |
| `tags`         | `list[str]`      |
| `created_at`   | `str`            |
| `is_current`   | `bool`           |

## DocumentVersionNotFoundError

`tai42_contract.versioning.errors.DocumentVersionNotFoundError`

```python theme={null}
class DocumentVersionNotFoundError(DocumentStoreError)
```

No version `version` exists for the `(kind, name)` document.

`version` is carried when known (the version-specific ops always know it);
the `(kind, name)` identity is always present, as for the other store errors.

**Attributes**

| Attribute | Type |
| --------- | ---- |
| `version` | —    |

## VersionedStore

`tai42_contract.versioning.VersionedStore`

```python theme={null}
class VersionedStore(Protocol)
```

Kind-discriminated, body-opaque version store. Identity is `(kind, name)`.

The `body` is opaque `dict`/JSONB — the store never inspects it; the caller
(a typed view) owns its shape. Every method raises loudly on a missing,
duplicate, or invalid target — never a silent default. `tags` is the generic
per-version grouping label (see `DocumentVersion`), distinct from any
categorization a `kind`'s body may carry.

### Members

#### create

`tai42_contract.versioning.VersionedStore.create`

```python theme={null}
async create(
    self,
    kind: str,
    name: str,
    body: dict[str, Any],
    tags: list[str] | None = None,
) -> DocumentRecord
```

Insert a new document at `active_version = 1` plus its version 1, one
transaction. Raise `DocumentExistsError` if `(kind, name)` is
already a live document.

**Parameters**

| Parameter | Type                | Default | Description |
| --------- | ------------------- | ------- | ----------- |
| `kind`    | `str`               | —       | —           |
| `name`    | `str`               | —       | —           |
| `body`    | `dict[str, Any]`    | —       | —           |
| `tags`    | `list[str] \| None` | `None`  | —           |

#### save\_version

`tai42_contract.versioning.VersionedStore.save_version`

```python theme={null}
async save_version(
    self,
    kind: str,
    name: str,
    body: dict[str, Any],
    tags: list[str] | None = None,
) -> DocumentVersion
```

Append a new version (`new_version = max(version) + 1`, NOT
`active_version + 1`) and bump the active pointer to it, one transaction
(a partial failure rolls the whole save back — no orphan version, no
half-bumped pointer). Raise `DocumentNotFoundError` if absent.

**Parameters**

| Parameter | Type                | Default | Description |
| --------- | ------------------- | ------- | ----------- |
| `kind`    | `str`               | —       | —           |
| `name`    | `str`               | —       | —           |
| `body`    | `dict[str, Any]`    | —       | —           |
| `tags`    | `list[str] \| None` | `None`  | —           |

#### list

`tai42_contract.versioning.VersionedStore.list`

```python theme={null}
async list(self, kind: str) -> list[DocumentRecord]
```

List the active (non-soft-deleted) documents of `kind`.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |

#### get

`tai42_contract.versioning.VersionedStore.get`

```python theme={null}
async get(self, kind: str, name: str) -> DocumentRecord
```

Fetch the active record for `(kind, name)`. Raise
`DocumentNotFoundError` if absent.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |
| `name`    | `str` | —       | —           |

#### get\_active\_body

`tai42_contract.versioning.VersionedStore.get_active_body`

```python theme={null}
async get_active_body(self, kind: str, name: str) -> dict[str, Any]
```

Return the `body` of the active version for `(kind, name)`. Raise
`DocumentNotFoundError` if absent.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |
| `name`    | `str` | —       | —           |

#### list\_versions

`tai42_contract.versioning.VersionedStore.list_versions`

```python theme={null}
async list_versions(self, kind: str, name: str) -> list[DocumentVersion]
```

List every version of `(kind, name)`, each carrying the
`DocumentVersion.is_current` signal derived from `active_version`.
Raise `DocumentNotFoundError` if the document is absent.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |
| `name`    | `str` | —       | —           |

#### get\_version

`tai42_contract.versioning.VersionedStore.get_version`

```python theme={null}
async get_version(self, kind: str, name: str, version: int) -> DocumentVersion
```

Fetch one version. Raise `DocumentVersionNotFoundError` if that
version does not exist.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |
| `name`    | `str` | —       | —           |
| `version` | `int` | —       | —           |

#### rollback

`tai42_contract.versioning.VersionedStore.rollback`

```python theme={null}
async rollback(self, kind: str, name: str, version: int) -> DocumentRecord
```

Re-point `active_version` to `version` (NO data copy). Raise
`DocumentVersionNotFoundError` if that version does not exist.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |
| `name`    | `str` | —       | —           |
| `version` | `int` | —       | —           |

#### soft\_delete

`tai42_contract.versioning.VersionedStore.soft_delete`

```python theme={null}
async soft_delete(self, kind: str, name: str) -> None
```

Set `is_active = False`, keeping the version history (audit). The
document drops out of `list`/`get` but its rows survive.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |
| `name`    | `str` | —       | —           |

#### delete

`tai42_contract.versioning.VersionedStore.delete`

```python theme={null}
async delete(self, kind: str, name: str) -> None
```

HARD delete, DISTINCT from `soft_delete`: remove ONLY the ACTIVE
document row for `(kind, name)` AND its version rows, so nothing of the
active document survives; a soft-deleted ghost of the same name is left
untouched. Raise `DocumentNotFoundError` if there is no active row.
Used to roll a never-succeeded create fully back and to remove a conflicted
record without leaving the ghost + spurious history a soft delete would.

**Parameters**

| Parameter | Type  | Default | Description |
| --------- | ----- | ------- | ----------- |
| `kind`    | `str` | —       | —           |
| `name`    | `str` | —       | —           |

#### rename

`tai42_contract.versioning.VersionedStore.rename`

```python theme={null}
async rename(self, kind: str, name: str, new_name: str) -> DocumentRecord
```

Re-key the ACTIVE document `(kind, name)` to `(kind, new_name)` in one
atomic write. The whole version history, every per-version `tags` label, and
the `active_version` pointer move untouched — versions key on the document,
not the name, so nothing is copied. The `body` is never inspected (the store
stays body-opaque). A soft-deleted ghost named `new_name` does NOT block (the
same partial-unique identity rule `create` follows), and a soft-deleted
ghost of `name` is left untouched as audit history. Raise
`DocumentNotFoundError` if `(kind, name)` has no active row; raise
`DocumentExistsError` (carrying `new_name`) if `(kind, new_name)` is
already a live document.

**Parameters**

| Parameter  | Type  | Default | Description |
| ---------- | ----- | ------- | ----------- |
| `kind`     | `str` | —       | —           |
| `name`     | `str` | —       | —           |
| `new_name` | `str` | —       | —           |
