kcode-k1-groups 0.1.0

K1 groups transaction facade and projection integration
Documentation
# K1 groups

`kcode-k1-groups` is the synchronous transaction facade for the logical KTO subsystem `k1-groups-subsystem`. It submits group mutations only through Peering and materializes canonical callbacks through `kcode-k1-groups-projection`.

## Public API

```rust
use std::{path::Path, sync::Arc};
use kcode_k1_groups::{
    Group, GroupId, GroupMemberships, GroupRevision, GroupRole, GroupUser, K1Groups,
    ModelId, TxId, UserId,
};
use kcode_k1_peering::K1Peering;
use kcode_k1_txn_ordering::K1TxnOrdering;

pub struct K1Groups;
impl K1Groups {
    pub fn open(
        root: &Path,
        ordering: Arc<K1TxnOrdering>,
        peering: Arc<K1Peering>,
    ) -> Result<Self, String>;
    pub fn create(&self, owner: UserId) -> Result<GroupRevision, String>;
    pub fn set_user_role(
        &self,
        actor: UserId,
        group: GroupId,
        user: UserId,
        role: Option<GroupRole>,
    ) -> Result<GroupRevision, String>;
    pub fn set_model_membership(
        &self,
        actor: UserId,
        group: GroupId,
        model: ModelId,
        present: bool,
    ) -> Result<GroupRevision, String>;
    pub fn get(&self, group: GroupId) -> Result<Option<Group>, String>;
    pub fn groups_for_user(&self, user: UserId) -> Result<Vec<GroupId>, String>;
    pub fn groups_for_model(&self, model: ModelId) -> Result<Vec<GroupId>, String>;
    pub fn memberships(
        &self,
        user: UserId,
        model: ModelId,
    ) -> Result<GroupMemberships, String>;
}
```

The package reexports only `Group`, `GroupId`, `GroupMemberships`, `GroupRevision`, `GroupRole`, `GroupUser`, `ModelId`, `TxId`, and `UserId` from the projection contract. Projection mutation and integration types remain private.

Every `UserId`, `GroupId`, `ModelId`, and `TxId` value is in the domain documented by the projection. The actor supplied to a mutation is trusted authenticated identity evidence from the caller. The facade does not authenticate accounts. IDs, revisions, transaction receipts, and operation IDs are not authority.

## Roles and results

Create makes its canonical callback transaction the new `GroupId` and gives the requested user the Owner role. An Admin may add or remove Users but cannot affect Admins or Owners or assign elevated roles. An Owner may perform human membership transitions except removing or demoting the final Owner. Users cannot mutate. Only Owners may change model membership. Authority is checked before an equal desired state is classified as unchanged.

Applied and Unchanged local callbacks return the projection's exact `GroupRevision`. Rejected callbacks return the projection's exact semantic reason as `Err`. Queries return complete owned results in unspecified order. No result is truncated.

## Wire contract

The subsystem ID is the canonical zero-padded `SubsystemId` for the exact logical text `k1-groups-subsystem`. Only strict version 1 payloads are accepted, with no trailing bytes:

| Action | Exact bytes | Length |
| --- | --- | --- |
| Create | `[1][1][operation_id:16][owner_user_txid:12]` | 30 |
| SetUserRole | `[1][2][operation_id:16][group_txid:12][actor_user_txid:12][target_user_txid:12][role:1]` | 55 |
| SetModelMembership | `[1][3][operation_id:16][group_txid:12][actor_user_txid:12][model_id:32][present:1]` | 75 |

Role values are 0 for absent, 1 for User, 2 for Admin, and 3 for Owner. Present is exactly 0 or 1. Every 12-byte human identity is reconstructed with `UserId::from_tx_id`. Unknown versions or kinds, wrong lengths, trailing bytes, and invalid discriminants fault the registration and instance.

Each local request obtains one fresh 16-byte operation ID from the operating-system CSPRNG. A collision with another in-process pending operation is rerolled without an arbitrary attempt limit. The ID is only synchronous internal correlation and is not exposed, persisted separately, accepted from callers, reused as an idempotency key, or treated as permission evidence. Randomness errors return without submission.

## Opening, persistence, and recovery

`open` first opens the durable projection at `root`, obtains its checkpoint, constructs the callback owner, and registers strictly after that checkpoint. An absent checkpoint requests genesis replay. Registration completes before return. The supplied Ordering and Peering handles are retained for the facade lifetime, and the Peering handle must target that same KTO instance.

Canonical callbacks parse once and call projection apply exactly once with the callback transaction ID. Remote canonical actions are applied normally. Rejected and Unchanged callbacks are callback successes because their global cursor is durable. Projection errors, malformed payloads, duplicate or contradictory correlation evidence, missing synchronous callbacks, callback/Peering transaction mismatches, and query errors make the facade unavailable until a fresh open.

A successful Peering submission must have produced one matching synchronous callback with the same transaction ID. On a Peering error, an already-recorded exact callback outcome wins and is returned without retry; otherwise the Peering error is returned unchanged. The facade submits at most once and never guesses whether to resubmit.

Reorganization first makes the old facade unavailable, invalidates pending local resolution, and clears only projection-owned derived state. It performs no live repair, replay, retry, or reopen. Fresh `open` is the recovery boundary.

## Concurrency and performance

Projection owns durable SQLite serialization and KTO owns callback ordering. The facade shares only brief availability and pending-correlation bookkeeping. It holds no facade lock across randomness, Peering, projection apply or clear, callback execution, query result construction, or waiting. There is no worker, background task, polling, timeout, retry, queue, or admission cap. Concurrent calls each submit independently; a slow call may wait in its own dependency lane but does not hold facade coordination needed by unrelated calls.

For a noncolliding mutation, facade-owned work is fixed: one 16-byte entropy fill, one payload construction of 30, 55, or 75 bytes, one pending hash-table entry, and exactly one synchronous Peering attempt. Collision handling repeats only entropy and brief reservation work. Peering, KTO callbacks, projection persistence, entropy, allocation, filesystem work, and lane waits have no finite wall-clock bound.

`open` inherits projection materialization and KTO replay work. `get` is average `O(1) + O(users + models)` for the addressed group. Reverse queries are average `O(1) + O(output)`. `memberships` intersects the smaller reverse set and is average `O(1) + O(smaller set + output)`. Queries perform no facade I/O and allocate only dependency-owned complete output plus possible error strings.

The managed-check `full_stack_mutations_queries_restart_and_cursor` and `many_to_many_reverse_and_intersection` fixtures in the hardened rootless Podman environment are the reproducible functional work fixtures. `concurrent_submissions_and_immediate_revocation` verifies independent concurrent entry and canonical authority ordering. No facade wall-clock promise includes dependency-owned storage, scheduling, entropy, or callback completion.

## Exclusions

This package does not implement Accounts, Invites, HTTP, authentication, Access Controller, model resolution, Server adoption, deployment, provider calls, names, nesting, deletion, metadata, migration, background reconciliation, or external retries. Projection source remains the authority for group invariants and durable query semantics.