kcode-k1-audio-classification-format 0.6.1

Stable facade for K1 audio-classification event and fragment formats
Documentation
# Audio classification formats

This crate directly re-exports the audio-classification transaction-event and local-fragment formats. Every callable below is owned by a re-exported format crate; this facade declares no callable and adds no implementation.

Version 0.6.1 exact-pins event-format 0.2.1 and fragment-format 0.2.2. Those leaves select `kcode-speaker-v3-analysis` 0.3.0 with default features disabled. Compatibility tests retain 0.2.0 as the byte reference and prove that the version-5 event bytes and version-2 fragment bytes are unchanged; the facade's wire schema versions and production implementation are unchanged.

## Public API

`PersonId`, `TxId`, `ExecutedAnalysis`, `FeatureVector24`, and `LocalSpeakerLabel` are also direct re-exports and are defined by their owning libraries.

```rust
pub enum FragmentStageV1 {
    Queue,
    Transcript,
    SpeakerLabels,
    SpeakerFeatures,
    Structuring,
    LabelConfirmation,
}

pub struct QueueV2 { pub audio_object_id: TxId }
pub struct ProgressV1 { pub fragment_id: TxId, pub update: ProgressUpdateV1 }

pub enum ProgressUpdateV1 {
    LlmJobStarted {
        sequence: u64,
        stage: FragmentStageV1,
        name: String,
    },
    LlmJobSucceeded { sequence: u64 },
    LlmJobFailed { sequence: u64, error: String },
    StageCompleted { stage: FragmentStageV1 },
}

pub struct TranscriptionCompleteV1 { pub fragment_id: TxId, pub analysis: ExecutedAnalysis }
pub struct FailedV2 {
    pub fragment_id: TxId,
    pub stage: FragmentStageV1,
    pub llm_job_sequence: Option<u64>,
    pub error: String,
}
pub struct DiscardedV2 { pub fragment_id: TxId }
pub struct SpeakerLabelV1 { pub speaker: LocalSpeakerLabel, pub person_id: Option<PersonId> }
pub struct LabelConfirmationV1 { pub fragment_id: TxId, pub interim_txid: TxId, pub speakers: Vec<SpeakerLabelV1> }

pub enum AudioClassificationEventV3 {
    Queue(QueueV2),
    Progress(ProgressV1),
    TranscriptionComplete(TranscriptionCompleteV1),
    Failed(FailedV2),
    Discarded(DiscardedV2),
    LabelConfirmation(LabelConfirmationV1),
}

pub struct StagedSpeakerV1 { pub speaker: LocalSpeakerLabel, pub language: String, pub features: FeatureVector24, pub usable_for_training: bool }
pub struct StagedFragmentV1 { pub analysis_txid: TxId, pub transcript: String, pub speakers: Vec<StagedSpeakerV1> }
pub struct FinalSpeakerV1 { pub speaker: LocalSpeakerLabel, pub person_id: Option<PersonId>, pub language: String, pub features: FeatureVector24, pub usable_for_training: bool }
pub struct FinalFragmentV1 { pub analysis_txid: TxId, pub confirmation_txid: TxId, pub transcript: String, pub speakers: Vec<FinalSpeakerV1> }

pub enum FormatError {
    Truncated,
    LengthOverflow,
    UnsupportedVersion(u8),
    UnknownEventTag(u8),
    InvalidEventBody,
    InvalidFragmentKind(u8),
    NonZeroReserved,
    NonZeroPadding,
    NonZeroStagedConfirmation,
    InvalidTxIdSlotLength(usize),
    InvalidUtf8,
    InvalidSpeakerLabel,
    InvalidFeatureBody,
    InvalidBoolean(u8),
    TrailingBytes,
    InvalidPath,
}

pub struct TxIdSlot { /* private fields */ }

impl TxIdSlot {
    pub const LEN: usize = 16;
    pub const PADDING_LEN: usize = 4;
    pub const fn new(txid: TxId) -> Self;
    pub const fn txid(self) -> TxId;
    pub fn encode(self) -> [u8; Self::LEN];
    pub fn decode(bytes: &[u8]) -> Result<Self, FormatError>;
}

pub fn encode_event(event: &AudioClassificationEventV3) -> Result<Vec<u8>, FormatError>;
pub fn decode_event(bytes: &[u8]) -> Result<AudioClassificationEventV3, FormatError>;
pub fn encode_staged_fragment(value: &StagedFragmentV1) -> Result<Vec<u8>, FormatError>;
pub fn decode_staged_fragment(bytes: &[u8]) -> Result<StagedFragmentV1, FormatError>;
pub fn encode_final_fragment(value: &FinalFragmentV1) -> Result<Vec<u8>, FormatError>;
pub fn decode_final_fragment(bytes: &[u8]) -> Result<FinalFragmentV1, FormatError>;
pub fn txid_path(txid: TxId) -> PathBuf;
pub fn txid_from_path(path: impl AsRef<Path>) -> Result<TxId, FormatError>;
```

`FragmentStageV1` implements `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `Serialize`, and `Deserialize`. `QueueV2`, `ProgressUpdateV1`, `FailedV2`, `DiscardedV2`, `SpeakerLabelV1`, and `LabelConfirmationV1` implement `Debug`, `Clone`, `PartialEq`, `Eq`, `Serialize`, and `Deserialize`. `ProgressV1` and `TranscriptionCompleteV1` implement `Debug`, `Clone`, `PartialEq`, `Serialize`, and `Deserialize`. `AudioClassificationEventV3`, `StagedSpeakerV1`, `StagedFragmentV1`, `FinalSpeakerV1`, and `FinalFragmentV1` implement `Debug`, `Clone`, and `PartialEq`. `FormatError` implements `Debug`, `Clone`, `PartialEq`, `Eq`, `Display`, and `Error`. `TxIdSlot` implements `Debug`, `Clone`, `Copy`, `PartialEq`, and `Eq`.

## Transaction events

`encode_event` and `decode_event` use envelope version byte `5`, followed by one stable tag byte and one postcard body. Tags are `1` Queue, `2` TranscriptionComplete, `3` Failed, `4` Discarded, `5` LabelConfirmation, and `6` Progress. Every event `TxId` and known `PersonId` serializes as exactly 12 raw bytes; `Some(PersonId)` is known and `None` is explicitly Unknown.

`decode_event` accepts only version 5, a known tag, one structurally valid body, and no trailing bytes. It returns the documented `FormatError` variants for malformed input and performs no semantic assignment or ordering validation.

## Local fragments and paths

The staged and final fragment codecs accept only format version 2. Bytes 0 through 15 contain version `2`, kind `1` staged or `2` final, and fourteen zero reserved bytes. Transaction slots retain their offsets and the body starts at byte 48.

Variable lengths and speaker counts are little-endian `u64`. Staged speakers and non-identity final-speaker fields retain their established order and encodings. A final identity is byte `0` for Unknown or byte `1` followed by the known `PersonId`'s 12 raw transaction-ID bytes.

`TxIdSlot` stores 12 ID bytes followed by four zero padding bytes. Fragment decoding returns the applicable `FormatError` for malformed input and performs no semantic validation of analysis values.

`txid_path` maps a `TxId` to `<first>/<remaining15>.dat` using a 16-character URL-safe unpadded base64 name. `txid_from_path` accepts only that two-component relative form. Neither function accesses a filesystem.

Event and fragment encoding and decoding use O(input plus output) work and output-proportional allocation; slot and path operations use bounded work and memory. The operations are stateless.