| 1 |
# mnw-assumptions |
| 2 |
|
| 3 |
MNW's business numbers as a typed, validated, substitutable source of truth. |
| 4 |
|
| 5 |
Loads `assumptions.toml`, computes derived values (Stripe fee math, ARPU, break-even, |
| 6 |
annual prices, per-creator marginal cost), checks the whole thing for internal |
| 7 |
consistency, and substitutes `{{ dotted.path | filter }}` markers in markdown. |
| 8 |
|
| 9 |
Sits on the generic substitution engine, [subst](../subst). Nothing here is reusable |
| 10 |
outside MNW. |
| 11 |
|
| 12 |
```rust |
| 13 |
use mnw_assumptions::Assumptions; |
| 14 |
|
| 15 |
let a = Assumptions::load("docs/business/assumptions.toml")?; |
| 16 |
a.validate()?; |
| 17 |
let resolved = a.substitute(&markdown)?; |
| 18 |
let html = docengine::render_permissive(&resolved); |
| 19 |
``` |
| 20 |
|
| 21 |
Canonical file: `MNW/server/docs/business/assumptions.toml`. The copy under |
| 22 |
`tests/fixtures/` is a vendored snapshot so the crate's own tests don't reach across the |
| 23 |
repo; `server/tests/assumptions.rs` is what tests the live one. |
| 24 |
|
| 25 |
## What it produces |
| 26 |
|
| 27 |
Three views of the same toml: |
| 28 |
|
| 29 |
- A **typed mirror** (serde structs) for the fields validation and derived math need. |
| 30 |
- A **derived registry** under the `derived.` prefix: `R_cap`, `ARPU_founding`, |
| 31 |
`ARPU_standard`, `stripe_fee_*`, `break_even_*`, `annual_<class>_<tier>`, |
| 32 |
`marginal_*`, the discount percentages. |
| 33 |
- A **flat lookup table** of every key in the file, dotted-path keyed, which is what |
| 34 |
substitution reads. Unknown sections are walked into it too, so a doc author can add an |
| 35 |
ad-hoc key to the toml and use it in markdown without touching Rust. |
| 36 |
|
| 37 |
## Validation |
| 38 |
|
| 39 |
`validate()` collects every failure rather than stopping at the first, and returns them |
| 40 |
as one `AssumptionsError::Validation(Vec<String>)`. The rules: |
| 41 |
|
| 42 |
- `expenses.F_monthly` inside (100, 10000), a typo guard on the burn rate |
| 43 |
- `tier_mix.assumed.*` sums to 1.0 |
| 44 |
- `reserve.surplus_split_{reserve,earnback}` sums to 1.0 |
| 45 |
- `reserve.rho_annual`, `reserve.rho_incident` inside (0, 1], and incident <= annual |
| 46 |
- every founding tier price <= the matching standard price |
| 47 |
- `cohort.cap_count > 0`, `cohort.cap_months > 0` |
| 48 |
- each `tier_bytes.<k>` parses back to the display string in `tier_limits.<k>` |
| 49 |
|
| 50 |
That last one is the shape to copy when a number has both a machine form and a |
| 51 |
human-facing form. The server adds one more of its own in `tests/assumptions.rs`: the |
| 52 |
`[synckit]` block must match the Rust constants in `src/synckit_billing.rs`, which stay |
| 53 |
authoritative. |
| 54 |
|
| 55 |
## Filters |
| 56 |
|
| 57 |
Everything `subst` ships (`int`, `ceil`, `floor`, `round`, `money`, `percent`, `upper`, |
| 58 |
`lower`), registered by default. Add MNW-specific ones with `with_filter(name, f)` before |
| 59 |
substituting. `LookupValue` is a re-export of `subst::Value`. |
| 60 |
|
| 61 |
## substitute_dir |
| 62 |
|
| 63 |
``` |
| 64 |
substitute_dir <assumptions.toml> <docs_root> [--check] |
| 65 |
``` |
| 66 |
|
| 67 |
Walks a markdown tree and substitutes in place. `--check` writes nothing and exits |
| 68 |
non-zero if any file would change or any marker failed to resolve. Built for the |
| 69 |
`_private/docs/` mirror, which cites business numbers but does not go through the boot-time |
| 70 |
pipeline the public site-docs corpus does. |
| 71 |
|
| 72 |
## License |
| 73 |
|
| 74 |
MIT. |
| 75 |
|