Forgiving loader from plain-JSON trace files into the canonical [amberfork_model::Run].
This is the input edge of the pipeline. docs/trace-format.md promises a deliberately
forgiving format: a step's unknown fields are preserved into attrs and surfaced as an
"unmapped attributes" warning rather than failing the parse. That forgiveness lives here,
not in the frozen model — [amberfork_model::Step] is strict and cannot hold unknown fields,
so this crate deserializes into private Raw* mirror types that capture the extras
(#[serde(flatten)]) and folds them into attrs during conversion.
What stays strict: the canonical kind vocabulary. A step whose kind is not one of
llm/tool/agent/other is a [IngestError::NotATrace], not a forgiven value — mapping
framework-specific span kinds down onto the canonical four is a normalizer's job, not this
canonical loader's.
Sync by design: this is std::fs only. tokio is reserved for the genuine async I/O edge
(record/serve), so the ingest path stays a pure, testable function.