# mnw-assumptions MNW's business numbers as a typed, validated, substitutable source of truth. Loads `assumptions.toml`, computes derived values (Stripe fee math, ARPU, break-even, annual prices, per-creator marginal cost), checks the whole thing for internal consistency, and substitutes `{{ dotted.path | filter }}` markers in markdown. Sits on the generic substitution engine, [subst](../subst). Nothing here is reusable outside MNW. ```rust use mnw_assumptions::Assumptions; let a = Assumptions::load("docs/business/assumptions.toml")?; a.validate()?; let resolved = a.substitute(&markdown)?; let html = docengine::render_permissive(&resolved); ``` Canonical file: `MNW/server/docs/business/assumptions.toml`. The copy under `tests/fixtures/` is a vendored snapshot so the crate's own tests don't reach across the repo; `server/tests/assumptions.rs` is what tests the live one. ## What it produces Three views of the same toml: - A **typed mirror** (serde structs) for the fields validation and derived math need. - A **derived registry** under the `derived.` prefix: `R_cap`, `ARPU_founding`, `ARPU_standard`, `stripe_fee_*`, `break_even_*`, `annual__`, `marginal_*`, the discount percentages. - A **flat lookup table** of every key in the file, dotted-path keyed, which is what substitution reads. Unknown sections are walked into it too, so a doc author can add an ad-hoc key to the toml and use it in markdown without touching Rust. ## Validation `validate()` collects every failure rather than stopping at the first, and returns them as one `AssumptionsError::Validation(Vec)`. The rules: - `expenses.F_monthly` inside (100, 10000), a typo guard on the burn rate - `tier_mix.assumed.*` sums to 1.0 - `reserve.surplus_split_{reserve,earnback}` sums to 1.0 - `reserve.rho_annual`, `reserve.rho_incident` inside (0, 1], and incident <= annual - every founding tier price <= the matching standard price - `cohort.cap_count > 0`, `cohort.cap_months > 0` - each `tier_bytes.` parses back to the display string in `tier_limits.` That last one is the shape to copy when a number has both a machine form and a human-facing form. The server adds one more of its own in `tests/assumptions.rs`: the `[synckit]` block must match the Rust constants in `src/synckit_billing.rs`, which stay authoritative. ## Filters Everything `subst` ships (`int`, `ceil`, `floor`, `round`, `money`, `percent`, `upper`, `lower`), registered by default. Add MNW-specific ones with `with_filter(name, f)` before substituting. `LookupValue` is a re-export of `subst::Value`. ## substitute_dir ``` substitute_dir [--check] ``` Walks a markdown tree and substitutes in place. `--check` writes nothing and exits non-zero if any file would change or any marker failed to resolve. Built for the `_private/docs/` mirror, which cites business numbers but does not go through the boot-time pipeline the public site-docs corpus does. ## License MIT.