kcode-k1-groups-domain 0.1.0

K1 group domain vocabulary
Documentation
# K1 groups domain

This library is the stateless public vocabulary for K1 groups. It owns identities, exact group-name validation, action and outcome values, and owned projection result values. It makes no authorization decision and owns no persistence, index, lock, recovery, migration, compatibility protocol, KTO parsing, I/O, callback, or generic abstraction.

## Public API

```rust
pub use kcode_k1_invites::UserId;
pub use kcode_k1_txn_ordering::TxId;

pub const ALL_USERS: GroupId;
pub const ALL_MODELS: GroupId;
pub const LOCAL_MODELS: GroupId;

pub enum SentinelGroup { AllUsers, AllModels, LocalModels }
pub struct GroupId { /* private fields */ }
pub struct ModelId { /* private fields */ }
pub struct GroupName { /* private fields */ }
pub enum GroupRole { User, Admin, Owner }

impl GroupId {
    pub const fn new(txid: TxId) -> Self;
    pub const fn txid(self) -> TxId;
    pub const fn sentinel(self) -> Option<SentinelGroup>;
}
impl ModelId {
    pub const fn from_bytes(bytes: [u8; 32]) -> Self;
    pub const fn as_bytes(&self) -> &[u8; 32];
    pub const fn into_bytes(self) -> [u8; 32];
}
impl GroupName {
    pub fn new(value: String) -> Result<Self, String>;
    pub fn as_str(&self) -> &str;
    pub fn into_string(self) -> String;
}

pub struct GroupUser { /* private fields */ }
pub struct GroupRevision { /* private fields */ }
pub struct Group { /* private fields */ }
pub struct GroupMemberships { /* private fields */ }

impl GroupUser { pub fn user_id(&self) -> UserId; pub fn role(&self) -> GroupRole; }
impl GroupRevision { pub fn group_id(&self) -> GroupId; pub fn txid(&self) -> TxId; }
impl Group {
    pub fn id(&self) -> GroupId;
    pub fn name(&self) -> &GroupName;
    pub fn revision(&self) -> &GroupRevision;
    pub fn users(&self) -> &[GroupUser];
    pub fn models(&self) -> &[ModelId];
}
impl GroupMemberships {
    pub fn revision(&self) -> Option<TxId>;
    pub fn user_groups(&self) -> &[GroupId];
    pub fn model_groups(&self) -> &[GroupId];
    pub fn shared_groups(&self) -> &[GroupId];
}

pub enum GroupAction {
    Create { owner: UserId, name: GroupName },
    Rename { group: GroupId, actor: UserId, name: GroupName },
    SetUserRole { group: GroupId, actor: UserId, user: UserId, role: Option<GroupRole> },
    SetModelMembership { group: GroupId, actor: UserId, model: ModelId, present: bool },
}
pub enum ApplyOutcome {
    Applied(GroupRevision),
    Unchanged(GroupRevision),
    Rejected(String),
}

pub mod projection_values {
    pub fn group_user(user_id: UserId, role: GroupRole) -> GroupUser;
    pub fn group_revision(group_id: GroupId, txid: TxId) -> GroupRevision;
    pub fn group(id: GroupId, name: GroupName, revision: GroupRevision, users: Vec<GroupUser>, models: Vec<ModelId>) -> Group;
    pub fn group_memberships(revision: Option<TxId>, user_groups: Vec<GroupId>, model_groups: Vec<GroupId>, shared_groups: Vec<GroupId>) -> GroupMemberships;
}
```

`SentinelGroup`, `GroupId`, `ModelId`, and `GroupRole` implement `Copy`, `Clone`, `Debug`, `Eq`, `Hash`, `Ord`, `PartialEq`, and `PartialOrd`. `GroupName` implements the same traits except `Copy`. `GroupUser`, `GroupRevision`, `Group`, `GroupMemberships`, `GroupAction`, and `ApplyOutcome` implement `Clone`, `Debug`, `Eq`, and `PartialEq`. Every struct field is private. `UserId` and `TxId` are the exact reexported types; the identity operations used with this API are `UserId::from_tx_id(TxId)`, `UserId::as_tx_id()`, `TxId::from_bytes([u8; 12])`, and `TxId::as_bytes()`.

`GroupName::new` accepts exact UTF-8 values of 1 through 128 bytes that contain no `char::is_control` character and at least one character for which `char::is_whitespace` is false. It returns exactly `"group name must be 1 through 128 UTF-8 bytes"`, `"group name must not contain control characters"`, or `"group name must contain a non-whitespace character"` for those failures, in that validation order. It does not trim, normalize, case-fold, or otherwise alter accepted text. Names are not unique.

The sentinel constants wrap `TxId` bytes `[255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 1]`, `[255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 2]`, and `[255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 3]`. `GroupId::sentinel` recognizes exactly those identities. They denote synthetic immutable groups; `LOCAL_MODELS` has no mutable population mechanism in this revision.

A `GroupUser` pairs one human identity with one role. A `GroupRevision` identifies an ordinary group and the transaction that last changed it. `Group` and `GroupMemberships` are owned results exposed completely by their accessors. Membership revision is the coherent projection cursor; user and model sets include their applicable synthetic group, while shared groups contain only ordinary membership. Result order is unspecified. `Applied` carries the new addressed-group revision, `Unchanged` carries the existing addressed-group revision, and `Rejected` carries the deterministic reason supplied by the state owner.

`projection_values` is the narrow integration surface for a projection state owner to construct the four private-field result values. Final facades do not reexport this module.

Every library-defined method and `projection_values` constructor is deterministic local `O(1)` work and allocation-free, except `GroupName::new`, which performs `O(bytes)` work over at most 128 accepted bytes and allocates only a returned error. Owned strings and vectors are moved without copying. All representable transaction, user, model, role, action, outcome, and vector values are in the type domain. Calls are independent and perform no disk or network I/O, locking, waiting, retry, timeout, callback, or background work. The managed-check `operation_performance` fixture in the hardened rootless Podman environment requires 100,000 representative constant-work operations and 10,000 maximum-length name validations to complete within five seconds per batch.