Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Shared semantic-fold library: pure decode-to-fact functions.
The event log already has one canonical bytes→struct decode path
([polyc_proto::events_decode]); what was missing was one canonical
fold on top of an already-decoded struct — the step that turns a
decoded wire message or a signed receipt into a fact with meaning ("this
is a tool call", "this receipt is verified"). Before this crate existed,
that fold was reimplemented independently by every consumer that wanted
it, and the reimplementations could (and did) disagree, including a
payment-receipt divergence this crate closes.
This crate owns exactly the fold: mechanical, keyed by a stable id, wanted in the same shape by two or more consumers. It has no I/O and no async: every function here is a pure, synchronous transform so a caller that can never fail open (a spend gate, a budget check) can call it directly without taking on a query engine's availability as a dependency.
Presentation stays with the consumer: truncation, snippet windowing, ordering, and size budgets are per-surface shaping this crate never does. Facts come out whole.
Two amendments to that charter, and why
Folds may decode. An earlier wording said callers pass already-decoded
structs. That was never true of the code — most entry points here take a
raw payload: &[u8] or an &Event and decode internally, and
[attribution_events_with_positions] takes positioned events — so the
sentence described an intention the crate had already outgrown. Decoding
belongs with the fold that gives the bytes meaning.
One projection lives here: commit scoping. [committed_turn_ids] and
[committed_message_facts] decide which turns are searchable and extract
their text. That is a policy, and [attribution_events]' own doc still
correctly says commit scoping "stays with the caller" for ITS purposes,
where consumers legitimately disagree about what committed means. The
narrow exception is for consumers that must NOT disagree: history
navigation and participation-scoped search answer the same question, so a
second implementation is a bug waiting for the two to drift. See
[committed_message_facts]'s own doc for the boundary.