khive-changeset 0.15.0

Producer-agnostic KG change-set op-list model and NDJSON-delta serialization
Documentation
# Restore a soft-deleted record

`RestoreOp` contains `target_id` and `record`, the complete live entity, note, or
edge expected after restoring a soft deletion. `record` uses the existing
`DeletePreimage` representation, tagged by `substrate`, and the same strict
embedded-record mirrors. All fields, including timestamps, status and expiry,
remain on the wire. This operation does not create a missing row.

The checked constructor and decoder require matching IDs, no `deleted_at`
tombstone, valid note salience/decay and valid edge weight. A note must have
`status: active`: the live restore operation cannot recover an archived status.
Unknown outer and embedded fields are refused. The wire tag is `op: restore`.

```json
{
  "op": "restore",
  "target_id": "00000000-0000-0000-0000-000000000001",
  "record": {
    "substrate": "entity",
    "id": "00000000-0000-0000-0000-000000000001",
    "namespace": "local",
    "created_at": 11,
    "updated_at": 29,
    "kind": "concept",
    "entity_type": null,
    "name": "Original",
    "description": null,
    "properties": {},
    "tags": [],
    "deleted_at": null
  }
}
```

This belongs to schema version 2; the envelope is the sole version carrier.
A version-1 decoder rejects the envelope before reading operations. The current
encoder also refuses a manually assigned version-1 envelope rather than emitting
a restore under the old contract.

## Inversion and lifecycle evidence

A soft delete inverts to a restore carrying its original full preimage. The
caller must supply the current tombstoned row. Content, identity, namespace,
creation time, properties, tags and expiry must still match. Entity and note
soft-deletion leave `updated_at` unchanged, so inversion checks it; edge
soft-deletion changes it, so inversion permits that difference. A deleted note
must have status `deleted`, corresponding to the captured active note.

A restore inverts to a soft delete carrying the actual current live record.
It checks all record content and creation time. Every live restore advances
`updated_at`, so that one timestamp may differ from the captured record. The
restored note must still be active. A full record is retained for review, not a
request to overwrite current storage clocks with historical timestamps.

The reverse walk rewinds content in a private working copy. When it crosses a
restore, it records the known prior tombstoned state separately: the operation
does not contain the original deletion timestamp, and inversion never invents
one. This supports a delete followed by restore of the same ID within one list.

Hard deletes and merges remain non-invertible. An archived-note soft delete also
refuses: the live surface cannot recover its old status. Missing or changed
records refuse with the original operation index; no partial inverse is returned.

This API is pure staging logic. It performs no live restoration or atomic
landing check. In particular, the domain record does not carry entity merge
provenance; the live applier must still refuse merge tombstones and enforce the
live restore's other guards. ADR-101/102's proposed restore amendment requires
approval before this format is adopted for landing.