kcode-k1-persons 0.2.0

Public K1 persons transaction facade
Documentation
# K1 persons

`K1Persons` provides local person mutations and coherent person reads through canonical transaction ordering.

## Public API

```rust
use std::{path::Path, sync::Arc};
use kcode_k1_peering::K1Peering;
use kcode_k1_txn_ordering::K1TxnOrdering;

pub use kcode_k1_persons_projection::PersonView;
pub use kcode_k1_persons_wire::PersonId;
pub use kcode_k1_txn_ordering::TxId;

pub struct K1Persons { /* private fields */ }

impl K1Persons {
    pub fn open(
        root: &Path,
        ordering: Arc<K1TxnOrdering>,
        peering: Arc<K1Peering>,
    ) -> Result<Self, String>;

    pub fn create(&self, name: String) -> Result<PersonId, String>;
    pub fn update(&self, person: PersonId, name: String) -> Result<(), String>;
    pub fn resolve(&self, canonical: PersonId, alias: PersonId) -> Result<PersonId, String>;
    pub fn read(&self, person: PersonId) -> Result<Option<PersonView>, String>;
    pub fn get(&self, person: PersonId) -> Result<Option<String>, String>;
}
```

```rust
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PersonView {
    pub person_id: PersonId,
    pub name: String,
}
```

`K1Persons` is `Send + Sync`. `PersonId` is copyable, debuggable, comparable, orderable, hashable, displayable, and parseable; it wraps a `TxId` and provides `pub const fn from_tx_id(txid: TxId) -> Self` and `pub const fn as_tx_id(self) -> TxId`. `TxId` is copyable, debuggable, comparable, orderable, hashable, and displayable.

## Identity and names

`create` commits a person whose ID is the committed transaction ID. Names preserve exact UTF-8 and validate in this order: 1 through 128 bytes, no control character, then at least one non-whitespace character. Validation errors are exactly `person name must be 1 through 128 UTF-8 bytes`, `person name must not contain control characters`, and `person name must contain a non-whitespace character`.

`update` accepts a canonical ID or alias and changes the current canonical person's name; assigning the existing name succeeds unchanged. An unknown ID returns `person is unknown`.

`resolve` checks `canonical` before `alias`, redirects the alias's entire current class to the canonical root, preserves the canonical root's name, and returns the resulting canonical ID. Resolving IDs already in one class succeeds unchanged. Unknown inputs return `canonical person is unknown` or `alias person is unknown` in that check order.

`read` accepts a canonical ID or alias. It returns `None` for an unknown ID; otherwise `PersonView.person_id` and `PersonView.name` are the current canonical root and its exact current name from one coherent projection snapshot. `get` is the name-only form of the same read.

## Lifecycle, transactions, and failure

`open` uses `root/persons.sqlite3`, resumes from its durable checkpoint, and registers the persons subsystem with `ordering`. The supplied `peering` must submit through that same live ordering instance. Missing person storage is initialized; malformed, incompatible, symlinked, or externally mutated storage is rejected without migration or repair.

Mutations submit through peering and return only after canonical ordering has synchronously delivered the transaction to the persons projection. Successful returns therefore include durable ordering and projection persistence. An error that identifies a transaction as committed is an ambiguous mutation outcome and must not be retried as though it were rejected before commitment.

Opening replays matching canonical transactions strictly after the stored checkpoint before live delivery. A canonical reorganization clears the projection and permanently makes that `K1Persons` instance unavailable; reopening rebuilds from canonical history.

Callback, correlation, poisoned-lock, or reorganization failure permanently faults the instance. Subsequent operations return `persons facade is unavailable`; the facade performs no retry or in-place recovery. Dependency errors are returned as strings without being reclassified.

Concurrent calls are supported. Same-subsystem mutations follow canonical callback order, and concurrent identical payloads are correlated with their individual transaction IDs. Reads are coherent but do not provide a transaction spanning separate calls.

## Performance

For `P` persisted persons and `N` canonical records, `open` performs `O(P + N)` recovery and replay work; reads are expected `O(1)` plus one name copy, create and update add `O(name bytes)` work, and resolve is `O(class size)`, in addition to synchronous ordering and persistence. Operations have no facade timeout or retry; SQLite contention may wait up to five seconds.

## Non-goals

This API does not provide deletion, person or alias listing, search, authorization, discovery, arbitrary transaction submission, peer ingestion, transaction queries, migration, legacy reading, background workers, polling, retry, network transport, or multiprocess coordination. It does not expose or authorize direct projection mutation, and package-local persistence is not a substitute for canonical ordering.