(** Serialization of import batches and promoted records for durable storage. The import use case is provisional: a batch holds raw third-party source, per-workout mappings and acknowledgements, and any promotions made so far. A repository must store and restore that state without the third-party parser, so this module encodes {!Hito_app.Workout_import.snapshot} — the parser-free view — and {!Hito_app.Repository.imported_record}, the canonical promoted evidence. The wire format is a length-prefixed field framing: every field is written as its byte length, a colon, then its raw bytes. The writer counts the length in bytes and copies the bytes verbatim. A field may therefore hold arbitrary UTF-8, newlines, commas, or colons. The format preserves the export source, an acknowledgement, a title, or an unsupported-row reason exactly. No {!Marshal}, no {!Obj}: the format is textual and self-describing. Timestamps reuse {!Timestamp}. Exercise ids resolve through {!Hito_core.Exercise.find_id}. A decode failure is a module-specific {!error}. A repository turns it into a fault, never a silent loss. *) open Hito_app type error = | Malformed of string (** The stored text does not parse. *) | Unknown_exercise of string (** A stored exercise id is not in the catalog. *) | Invalid_time (** A stored [ended_at] precedes its [started_at]. *) val pp_error : Format.formatter -> error -> unit val encode_snapshot : Workout_import.snapshot -> string (** A stable, single-string encoding of an import batch snapshot. *) val decode_snapshot : find_exercise:(Exercise.id -> Exercise.t option) -> string -> (Workout_import.batch, error) result (** Decode a batch snapshot and restore the opaque batch, resolving each exercise id through [find_exercise]. *) val encode_record : Repository.imported_record -> string (** A stable, single-string encoding of a promoted record. *) val decode_record : string -> (Repository.imported_record, error) result (** Decode a promoted record, resolving each observation's exercise id through {!Hito_core.Exercise.find_id}. *)