1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! florecon — incremental financial reconciliation as a conserving strategy algebra.
//!
//! The centerpiece is the [`strategy`] algebra: `Strategy: Bag -> (Groups,
//! residual)`, conserving by construction. Everything else is scaffolding around
//! it.
//!
//! - [`strategy`] — **the spine.** A combinator algebra over an unordered bag of
//! items. Cheap deterministic primitives (`exact_1to1`, `agg_net`,
//! `signal_group`, `running_zero`, `pivot`) cascade and shard
//! via `seq`, `partition_by`, `when`, and `windowed`. One leaf —
//! [`strategy::flow`] — is the min-cost-flow arbiter: describe a domain via a
//! [`strategy::FlowSpec`] and the leaf drives the [`engine`] over it. The
//! engine is just that one node's implementation.
//! - [`engine`] — the network-simplex transportation solver behind the `flow`
//! node: stable [`engine::NodeId`]/[`engine::ArcId`] handles, warm-started
//! re-solving, [`engine::Snapshot`] persistence. Frozen because it is a
//! hard-won correct algorithm, not because it is central.
//! - [`recon`] — the stateful facade [`recon::Recon`] around *any* strategy:
//! `upsert` / `remove` / `solve` / `pin` / `merge` / `detach` / `dissolve`,
//! returning an allocation-hypergraph [`Report`]. Two orthogonal axes
//! (proposed vs pinned lifecycle, provenance label) replace ad-hoc status
//! strings; conservation is enforced at the boundary.
//! - [`sdk`] — the plugin authoring surface: implement [`sdk::Plugin`] (project a
//! columnar Arrow [`sdk::Table`] the host ships into typed items, name a stable
//! [`sdk::Plugin::id`], pick a [`strategy`]), then `export_plugin!` emits a
//! self-describing wasm module. The declared schema is enforced at ingest, so
//! a mistyped column fails loudly instead of corrupting the ledger.
//!
//! Enable the `serde` feature to serialize an [`engine::Snapshot`] and
//! warm-start off a persisted basis.
//!
//! ```
//! use florecon::strategy::{flow, FlowSpec, Item};
//!
//! #[derive(Clone)]
//! struct Tx { date: i64 }
//!
//! let spec = FlowSpec::new()
//! .penalty(1e6)
//! .window(3)
//! .block_key(|t: &Tx| t.date)
//! .cost(|a: &Tx, b: &Tx| Some(1.0 + (a.date - b.date).abs() as f64));
//!
//! // The conserved amount rides on `Item::amount`, not the spec.
//! let s = flow(spec);
//! let r = s.run(vec![Item::new(1, 100, Tx { date: 0 }), Item::new(2, -100, Tx { date: 0 })]);
//! assert_eq!(r.groups[0].net, 0); // nets clean
//! ```
pub use ;
pub use ApiError;
pub use ;
pub use Recon;
pub use ;