Skip to main content

max / makenotwork

2.9 KB · 75 lines History Blame Raw
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