Skip to main content

max / makenotwork

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