Skip to main content

majordomus_cli/economics/
report.rs

1//! The human-readable benchmark report, `docs/generated/economics.md`: a rendering of one
2//! [`EconomicsSummary`] and nothing else. Every number in it is a field of the summary,
3//! spelled for a reader; the report computes none, so it cannot disagree with the CLI, the
4//! API, MCP, the Cockpit or the site, which render the same summary.
5//!
6//! ```
7//! use majordomus_cli::economics::{report, summarize};
8//! let dir = tempfile::tempdir().unwrap();
9//! let text = report::markdown(&summarize(dir.path(), &Default::default()));
10//! assert!(text.contains("No verified total-token-savings claim is available"));
11//! ```
12
13use std::fmt::Write as _;
14
15use serde::Serialize;
16
17use super::model::{
18    EconomicsInterval, EconomicsMetric, EconomicsMetricStatus, EconomicsPairStatus,
19    EconomicsSummary,
20};
21use super::stats::{level_text, percent_text};
22use super::{wire, CONTEXT_REDUCTION};
23
24/// A signed whole number, with no sign at all when it rounds to zero: a figure that is,
25/// to the precision shown, no difference never reads as `+0` or `-0`.
26fn signed_count(v: f64) -> String {
27    if !v.is_finite() {
28        return "n/a".into();
29    }
30    let whole = v.round();
31    if whole == 0.0 {
32        "0".into()
33    } else {
34        format!("{whole:+.0}")
35    }
36}
37
38/// A figure in its unit, as every surface spells it. A `ratio` is a signed percentage with
39/// one decimal ([`percent_text`]); any other unit is a signed whole number followed by the
40/// unit. Neither carries a sign when it rounds to zero, and a value that is not a number
41/// prints `n/a`, so no surface ever shows `-0.0%` or a figure nobody computed.
42///
43/// Every ratio the calculator derives from a pair is a reduction, `1 - treatment /
44/// control`: positive means Majordomus used less, and a negative reduction means
45/// Majordomus used more. The spelling keeps that sign and never flips it.
46///
47/// ```
48/// use majordomus_cli::economics::report::amount;
49/// assert_eq!(amount(0.2, "ratio"), "+20.0%");
50/// assert_eq!(amount(-0.1, "ratio"), "-10.0%", "a negative reduction stays negative");
51/// assert_eq!(amount(-0.0004, "ratio"), "0.0%", "never -0.0%");
52/// assert_eq!(amount(-1500.4, "tokens"), "-1500 tokens");
53/// assert_eq!(amount(0.3, "tokens"), "0 tokens");
54/// assert_eq!(amount(f64::NAN, "ratio"), "n/a");
55/// ```
56pub fn amount(v: f64, unit: &str) -> String {
57    if unit == "ratio" {
58        percent_text(v)
59    } else {
60        format!("{} {unit}", signed_count(v))
61    }
62}
63
64/// A metric's value as the report spells it: [`amount`] in the metric's unit, and an
65/// absent value the words `not measured`, so that an unmeasured metric never reads as a
66/// change of zero.
67///
68/// ```
69/// use majordomus_cli::economics::{model::EconomicsMetric, report};
70/// let m: EconomicsMetric = serde_json::from_value(serde_json::json!({
71///     "id": "effective_token_reduction", "title": "Effective token reduction",
72///     "class": "derived", "unit": "ratio", "formula": "1 - treatment / control",
73///     "value": 0.2, "status": "preliminary", "n": 3, "suite": "pilot"
74/// }))
75/// .unwrap();
76/// assert_eq!(report::value(&m), "+20.0%");
77/// assert_eq!(report::value(&EconomicsMetric { value: None, ..m }), "not measured");
78/// ```
79pub fn value(m: &EconomicsMetric) -> String {
80    match m.value {
81        None => "not measured".into(),
82        Some(v) => amount(v, &m.unit),
83    }
84}
85
86/// An interval as every surface spells it: the confidence level ([`level_text`], so that
87/// `9750` reads `97.5%` and is never rounded into a level the methodology did not
88/// declare), then both bounds in the metric's unit ([`amount`]). The bounds of a ratio are
89/// reductions, like the value they bracket.
90///
91/// ```
92/// use majordomus_cli::economics::{model::EconomicsInterval, report::interval_text};
93/// let i = EconomicsInterval {
94///     level_bp: 9750, low: -0.05, high: 0.3, method: "percentile bootstrap".into(),
95///     resamples: 2000, seed: 7,
96/// };
97/// assert_eq!(interval_text(&i, "ratio"), "97.5%: -5.0% to +30.0%");
98/// let tokens = EconomicsInterval { level_bp: 9500, low: 1200.0, high: 4800.0, ..i };
99/// assert_eq!(interval_text(&tokens, "tokens"), "95%: +1200 tokens to +4800 tokens");
100/// ```
101pub fn interval_text(i: &EconomicsInterval, unit: &str) -> String {
102    format!(
103        "{}: {} to {}",
104        level_text(i.level_bp),
105        amount(i.low, unit),
106        amount(i.high, unit)
107    )
108}
109
110/// An enum of the summary as a person reads it: its serialised name ([`wire`], the one the
111/// API and MCP answer with) with each underscore a space, so `no_evidence` reads `no
112/// evidence`. A surface never spells a state from `Debug`, which would print
113/// `NoEvidence`, a word the API never uses.
114///
115/// ```
116/// use majordomus_cli::economics::model::{EconomicsFreshness, EconomicsPairStatus};
117/// use majordomus_cli::economics::report::words;
118/// assert_eq!(words(&EconomicsFreshness::NoEvidence), "no evidence");
119/// assert_eq!(words(&EconomicsPairStatus::ControlFailed), "control failed");
120/// assert_eq!(words(&EconomicsPairStatus::Valid), "valid");
121/// ```
122pub fn words<T: Serialize>(value: &T) -> String {
123    wire(value).replace('_', " ")
124}
125
126fn cell(s: &str) -> String {
127    s.replace('|', "\\|").replace('\n', " ")
128}
129
130/// The whole report for one summary, as Markdown. It reads nothing but `s`, so the same
131/// summary always renders to the same bytes. When no methodology is declared (`present` is
132/// false) the report stops after the verdict, its unmet thresholds and the records that
133/// could not be read, because every later section would describe evidence that cannot
134/// exist. Free text placed in a table cell has its pipes escaped and its line breaks
135/// flattened, so a reason can never split a row. Every per-pair and aggregate token figure
136/// is a reduction, spelled by [`amount`]: a negative reduction means Majordomus used more
137/// tokens, and no figure is negated on the way to the page.
138///
139/// ```
140/// use majordomus_cli::economics::{report, summarize};
141/// let dir = tempfile::tempdir().unwrap();
142/// let s = summarize(dir.path(), &Default::default());
143/// let text = report::markdown(&s);
144/// assert!(text.starts_with("# Token economics: benchmark report"));
145/// assert!(text.contains(&s.verdict.statement));
146/// assert!(!s.present && !text.contains("## Metrics"));
147/// ```
148pub fn markdown(s: &EconomicsSummary) -> String {
149    let mut o = String::new();
150    let _ = writeln!(o, "# Token economics: benchmark report\n");
151    let _ = writeln!(o, "Generated from recorded evidence by `majordomus generate economics`. Every figure below is computed by one calculator (`apps/majordomus-cli/src/economics`) from the raw runs under `.ai/repo/benchmarks/economics/runs/`; nothing here is typed by hand.\n");
152    let _ = writeln!(o, "## Verdict\n");
153    let _ = writeln!(o, "{}\n", s.verdict.statement);
154    if !s.verdict.unmet.is_empty() {
155        let _ = writeln!(o, "The publication rule is not met:\n");
156        for u in &s.verdict.unmet {
157            let _ = writeln!(o, "- {u}");
158        }
159        let _ = writeln!(o);
160    }
161    if !s.diagnostics.is_empty() {
162        let _ = writeln!(o, "## Records that could not be read\n");
163        let _ = writeln!(o, "Nothing in them is counted anywhere below.\n");
164        for d in &s.diagnostics {
165            let _ = writeln!(o, "- {d}");
166        }
167        let _ = writeln!(o);
168    }
169    if !s.present {
170        return o;
171    }
172    let _ = writeln!(o, "## Question and methodology\n");
173    if let Some(q) = &s.question {
174        let _ = writeln!(o, "{q}\n");
175    }
176    let _ = writeln!(
177        o,
178        "Methodology version {}; primary metric `{}`. The methodology is `.ai/repo/benchmarks/economics/methodology.yaml`.\n",
179        s.methodology.unwrap_or(0),
180        s.primary_metric.clone().unwrap_or_default()
181    );
182    let _ = writeln!(o, "### Control and treatment\n");
183    for v in &s.variants {
184        let _ = writeln!(
185            o,
186            "- **{}** ({}, `{}`): {}",
187            v.title, v.role, v.id, v.description
188        );
189        if !v.excludes.is_empty() {
190            let _ = writeln!(o, "  Excludes: {}.", v.excludes.join(", "));
191        }
192    }
193    let _ = writeln!(o);
194    if let Some(p) = &s.publication {
195        let _ = writeln!(o, "### When a total-token claim may be published\n");
196        let _ = writeln!(
197            o,
198            "At least {} valid matched pairs; at least {} task categories with {} valid pairs each; at least {} repetitions of every task and variant; at least {} in 10000 attempted pairs valid; a bootstrap interval no wider than {} in 10000; evidence {}; no run recorded from a working tree with uncommitted changes.\n",
199            p.min_valid_pairs,
200            p.min_categories,
201            p.min_pairs_per_category,
202            p.min_repetitions,
203            p.min_valid_pair_rate_bp,
204            p.max_interval_width_bp,
205            if p.require_current { "current" } else { "of any age" }
206        );
207    }
208    let _ = writeln!(o, "## Evidence\n");
209    let _ = writeln!(o, "| suite | kind | freshness | runs | valid pairs | attempted | control failed | treatment failed | both failed | other | revisions | harness | models |");
210    let _ = writeln!(o, "|---|---|---|---|---|---|---|---|---|---|---|---|---|");
211    for v in &s.suites {
212        let _ = writeln!(
213            o,
214            "| {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} |",
215            v.id,
216            v.kind,
217            words(&v.freshness),
218            v.runs,
219            v.pairs.valid,
220            v.pairs.attempted,
221            v.pairs.control_failed,
222            v.pairs.treatment_failed,
223            v.pairs.both_failed,
224            v.pairs.other,
225            v.revisions
226                .iter()
227                .map(|r| format!("`{}`", &r[..r.len().min(12)]))
228                .collect::<Vec<_>>()
229                .join(" "),
230            v.harnesses.join(", "),
231            v.models_reported.join(", ")
232        );
233    }
234    let _ = writeln!(o);
235    for v in s.suites.iter().filter(|v| v.freshness_detail.is_some()) {
236        let _ = writeln!(
237            o,
238            "- `{}`: {}",
239            v.id,
240            v.freshness_detail.as_deref().unwrap_or("")
241        );
242    }
243    let _ = writeln!(o, "\n## Metrics\n");
244    let _ = writeln!(
245        o,
246        "| metric | value | status | class | n | interval | what it is not |"
247    );
248    let _ = writeln!(o, "|---|---|---|---|---|---|---|");
249    for m in &s.metrics {
250        let class = match m.inputs {
251            Some(i) => format!("{} from {}", m.class.word(), i.word()),
252            None => m.class.word().to_string(),
253        };
254        let interval = m
255            .interval
256            .as_ref()
257            .map(|i| interval_text(i, &m.unit))
258            .unwrap_or_else(|| "—".into());
259        let _ = writeln!(
260            o,
261            "| `{}` | {} | {} | {} | {} | {} | {} |",
262            m.id,
263            value(m),
264            words(&m.status),
265            class,
266            m.n,
267            interval,
268            cell(m.not.as_deref().unwrap_or(""))
269        );
270    }
271    let _ = writeln!(o, "\nFormulas and warnings:\n");
272    for m in &s.metrics {
273        let _ = writeln!(o, "- `{}`: {}.", m.id, m.formula);
274        if let Some(i) = &m.interval {
275            let _ = writeln!(
276                o,
277                "  - interval: {}; {} resamples, seed {}.",
278                i.method, i.resamples, i.seed
279            );
280        }
281        for w in &m.warnings {
282            let _ = writeln!(o, "  - {w}");
283        }
284    }
285    let _ = writeln!(o);
286    if !s.segments.is_empty() {
287        let _ = writeln!(o, "## Segments\n");
288        let _ = writeln!(o, "Median per-pair total-token reduction over valid pairs, by segment: positive means Majordomus used fewer tokens, and a negative reduction means Majordomus used more tokens.\n");
289        let _ = writeln!(
290            o,
291            "| dimension | segment | valid pairs | median reduction | lowest | highest |"
292        );
293        let _ = writeln!(o, "|---|---|---|---|---|---|");
294        for g in &s.segments {
295            if let Some(d) = &g.token_reduction {
296                let _ = writeln!(
297                    o,
298                    "| {} | {} | {} | {} | {} | {} |",
299                    g.dimension,
300                    g.value,
301                    g.n,
302                    percent_text(d.median),
303                    percent_text(d.min),
304                    percent_text(d.max)
305                );
306            }
307        }
308        let _ = writeln!(o);
309    }
310    let live: Vec<_> = s.pairs.iter().collect();
311    if !live.is_empty() {
312        let _ = writeln!(o, "## Pairs\n");
313        let _ = writeln!(o, "Every declared pair, valid or not. Tokens are the provider-reported totals of every session of the run.\n");
314        let _ = writeln!(o, "| task | category | rep | status | control tokens | treatment tokens | token reduction | cost reduction | first-request overhead | reasons |");
315        let _ = writeln!(o, "|---|---|---|---|---|---|---|---|---|---|");
316        for p in live {
317            let _ = writeln!(
318                o,
319                "| {} | {} | {} | {} | {} | {} | {} | {} | {} | {} |",
320                p.task,
321                p.category,
322                p.repetition,
323                words(&p.status),
324                p.control_usage
325                    .as_ref()
326                    .map(|u| u.total.to_string())
327                    .unwrap_or_else(|| "—".into()),
328                p.treatment_usage
329                    .as_ref()
330                    .map(|u| u.total.to_string())
331                    .unwrap_or_else(|| "—".into()),
332                p.token_reduction
333                    .map(percent_text)
334                    .unwrap_or_else(|| "—".into()),
335                p.cost_reduction
336                    .map(percent_text)
337                    .unwrap_or_else(|| "—".into()),
338                p.first_request_overhead
339                    .map(|x| signed_count(x as f64))
340                    .unwrap_or_else(|| "—".into()),
341                cell(&p.reasons.join("; "))
342            );
343        }
344        let _ = writeln!(o, "\n\"Token reduction\" is `1 - treatment / control` on total tokens, and \"cost reduction\" the same on the harness's cost projection: positive means Majordomus used fewer tokens, and a negative reduction means Majordomus used more tokens. \"First-request overhead\" is not a reduction: it is the treatment's first-request input minus the control's, so positive means Majordomus added tokens before the model acted. An excluded pair shows its reductions so that the outlier rule's effect is visible; only valid pairs enter the metrics.\n");
345        let valid = s
346            .pairs
347            .iter()
348            .filter(|p| p.status == EconomicsPairStatus::Valid)
349            .count();
350        let _ = writeln!(
351            o,
352            "{valid} of {} declared pair(s) are valid.\n",
353            s.pairs.len()
354        );
355    }
356    if let Some(c) = &s.context {
357        let _ = writeln!(o, "## Context selection (deterministic)\n");
358        let _ = writeln!(
359            o,
360            "At revision `{}`, {} seeds (the issues of this repository's plan) were compiled by `majordomus devcontext` under its default budget of {} (bytes-over-four) tokens. Counted with {} ({}): {} candidate tokens (files the compiler judged relevant), {} selected; {} tokens reached in all. The compiler's own estimate of the selected tokens was {}; {} seed(s) exceed the budget when counted.\n",
361            &c.revision[..c.revision.len().min(12)],
362            c.seeds,
363            c.budget_tokens,
364            c.tokenizer.encoding,
365            c.tokenizer.implementation,
366            c.candidate_tokens,
367            c.selected_tokens,
368            c.considered_tokens,
369            c.estimated_selected_tokens,
370            c.seeds_over_budget_counted
371        );
372        let _ = writeln!(o, "This is context *selection*, not total token savings: it says what the compiler put in front of a worker out of what it found relevant, not what a session consumed.\n");
373        let unmeasured = s
374            .metrics
375            .iter()
376            .find(|m| m.id == CONTEXT_REDUCTION)
377            .filter(|m| m.status == EconomicsMetricStatus::NotMeasured);
378        if let Some(m) = unmeasured {
379            let _ = writeln!(
380                o,
381                "No figure is taken from this record: `{}` is not measured ({}).\n",
382                m.id,
383                m.warnings.join("; ")
384            );
385        }
386    }
387    if !s.history.is_empty() {
388        let _ = writeln!(o, "## History\n");
389        let _ = writeln!(
390            o,
391            "| suite | methodology | revision | recorded | metric | value | n |"
392        );
393        let _ = writeln!(o, "|---|---|---|---|---|---|---|");
394        for h in &s.history {
395            let _ = writeln!(
396                o,
397                "| {} | {} | `{}` | {} | `{}` | {} | {} |",
398                h.suite,
399                h.methodology,
400                &h.revision[..h.revision.len().min(12)],
401                h.at,
402                h.metric,
403                percent_text(h.value),
404                h.n
405            );
406        }
407        let _ = writeln!(o);
408    }
409    let _ = writeln!(o, "## Hypotheses\n");
410    let _ = writeln!(o, "Stated before any evidence existed, so that the evidence can refute them. They are not results.\n");
411    for h in &s.hypotheses {
412        let _ = writeln!(o, "- `{}` ({}): {}", h.id, h.status, h.statement);
413    }
414    let _ = writeln!(o, "\n## Reproduce\n");
415    let _ = writeln!(o, "```sh");
416    let _ = writeln!(o, "majordomus economics references        # every task fails on its start and passes on its reference; no model");
417    let _ = writeln!(
418        o,
419        "majordomus economics measure            # the context suite; deterministic, no model"
420    );
421    let _ = writeln!(o, "majordomus economics run --suite pilot  # live sessions; needs a Claude Code login and spends usage");
422    let _ = writeln!(
423        o,
424        "majordomus economics summary            # recompute every figure from the recorded runs"
425    );
426    let _ = writeln!(o, "majordomus economics explain effective_token_reduction");
427    let _ = writeln!(o, "```\n");
428    let _ = writeln!(o, "## Limitations\n");
429    let _ = writeln!(o, "- One fixture repository, small by design; a large repository is where context selection matters most, and it is not measured here.");
430    let _ = writeln!(o, "- One harness (Claude Code) and the model each suite names. Tokens are the provider's; counts from different providers are never compared directly.");
431    let _ = writeln!(o, "- Sessions are stochastic. A pair is one sample of each arm; only the distribution over many pairs says anything.");
432    let _ = writeln!(
433        o,
434        "- Cost is the harness's own projection at run time, not a bill."
435    );
436    let _ = writeln!(
437        o,
438        "- Context reduction is measured against what the compiler itself judged relevant."
439    );
440    let _ = writeln!(o, "- The context suite counts the corpus at the revision it recorded, not at the current one: a later change to the plan or to the files it selects from moves nothing until the suite is measured again.");
441    let _ = writeln!(o, "- The treatment is Majordomus as a user who installs only the shell tool gets it, and its generated bootstrap advises commands that installation cannot run (`majordomus worktree`). What a session spends on them is part of what that user gets, and it is measured as such, not corrected for.");
442    o
443}
444
445#[cfg(test)]
446mod tests {
447    use super::*;
448    use crate::economics::model::{EconomicsClass, EconomicsPair, EconomicsVerdict};
449
450    fn metric(unit: &str, value: Option<f64>) -> EconomicsMetric {
451        EconomicsMetric {
452            id: "effective_token_reduction".into(),
453            title: "Effective token reduction".into(),
454            class: EconomicsClass::Derived,
455            inputs: Some(EconomicsClass::Observed),
456            unit: unit.into(),
457            formula: "1 - treatment / control".into(),
458            not: None,
459            value,
460            status: EconomicsMetricStatus::Preliminary,
461            n: 3,
462            distribution: None,
463            interval: None,
464            suite: "pilot".into(),
465            evidence: vec![],
466            warnings: vec![],
467        }
468    }
469
470    fn pair(reasons: &[&str]) -> EconomicsPair {
471        EconomicsPair {
472            suite: "pilot".into(),
473            task: "vat-rounding".into(),
474            category: "bugfix".into(),
475            sessions: 1,
476            repetition: 1,
477            control: None,
478            treatment: None,
479            status: EconomicsPairStatus::Incomparable,
480            reasons: reasons.iter().map(|r| r.to_string()).collect(),
481            control_usage: None,
482            treatment_usage: None,
483            token_reduction: None,
484            cost_reduction: None,
485            tool_call_reduction: None,
486            continuation_reduction: None,
487            first_request_overhead: None,
488        }
489    }
490
491    fn summary(present: bool, pairs: Vec<EconomicsPair>) -> EconomicsSummary {
492        EconomicsSummary {
493            present,
494            methodology: Some(1),
495            question: Some("How many tokens does a session consume?".into()),
496            primary_metric: Some("effective_token_reduction".into()),
497            verdict: EconomicsVerdict {
498                publishable: false,
499                statement: "No verified total-token-savings claim is available.".into(),
500                unmet: vec!["at least 30 valid matched pairs".into()],
501            },
502            metrics: vec![metric("ratio", Some(0.2))],
503            suites: vec![],
504            pairs,
505            segments: vec![],
506            context: None,
507            history: vec![],
508            variants: vec![],
509            hypotheses: vec![],
510            publication: None,
511            diagnostics: vec![],
512        }
513    }
514
515    #[test]
516    fn a_ratio_is_a_signed_percentage_with_one_decimal() {
517        assert_eq!(value(&metric("ratio", Some(0.2))), "+20.0%");
518        assert_eq!(value(&metric("ratio", Some(-0.125))), "-12.5%");
519        assert_eq!(value(&metric("ratio", Some(0.0))), "0.0%");
520        assert_eq!(
521            value(&metric("ratio", Some(-0.0004))),
522            "0.0%",
523            "never -0.0%"
524        );
525    }
526
527    #[test]
528    fn a_count_carries_its_unit_and_an_absent_value_is_not_measured() {
529        assert_eq!(value(&metric("tokens", Some(1500.0))), "+1500 tokens");
530        assert_eq!(value(&metric("calls", Some(-4.0))), "-4 calls");
531        assert_eq!(value(&metric("ratio", None)), "not measured");
532        assert_eq!(value(&metric("tokens", None)), "not measured");
533    }
534
535    #[test]
536    fn without_a_methodology_the_report_stops_after_the_verdict() {
537        let text = markdown(&summary(false, vec![pair(&["control run missing"])]));
538        assert!(text.contains("## Verdict"));
539        assert!(text.contains("No verified total-token-savings claim is available."));
540        assert!(text.contains("- at least 30 valid matched pairs"));
541        assert!(text
542            .trim_end()
543            .ends_with("- at least 30 valid matched pairs"));
544        for later in [
545            "## Question and methodology",
546            "## Evidence",
547            "## Metrics",
548            "## Pairs",
549        ] {
550            assert!(
551                !text.contains(later),
552                "{later} rendered without a methodology"
553            );
554        }
555        assert!(!text.contains("## Limitations"));
556    }
557
558    #[test]
559    fn a_pipe_inside_a_reason_is_escaped_in_the_pairs_table() {
560        let text = markdown(&summary(
561            true,
562            vec![pair(&["model a|b differs", "no usage"])],
563        ));
564        let row = text
565            .lines()
566            .find(|l| l.starts_with("| vat-rounding |"))
567            .expect("the pair has a row");
568        assert!(row.ends_with("| model a\\|b differs; no usage |"), "{row}");
569        let unescaped = row.replace("\\|", "");
570        assert_eq!(unescaped.matches('|').count(), 11, "{row}");
571    }
572
573    fn valid(reduction: f64) -> EconomicsPair {
574        EconomicsPair {
575            status: EconomicsPairStatus::Valid,
576            reasons: vec![],
577            token_reduction: Some(reduction),
578            cost_reduction: Some(reduction),
579            first_request_overhead: Some(0),
580            ..pair(&[])
581        }
582    }
583
584    #[test]
585    fn a_pair_shows_its_reduction_with_the_sign_the_calculator_gave_it() {
586        let text = markdown(&summary(
587            true,
588            vec![valid(-0.1), valid(0.25), valid(-0.0001)],
589        ));
590        assert!(
591            text.contains("| token reduction | cost reduction |"),
592            "{text}"
593        );
594        assert!(!text.contains("token change"), "{text}");
595        let rows: Vec<&str> = text
596            .lines()
597            .filter(|l| l.starts_with("| vat-rounding |"))
598            .collect();
599        assert_eq!(rows.len(), 3);
600        assert!(
601            rows[0].contains("| valid | — | — | -10.0% | -10.0% | 0 |"),
602            "{}",
603            rows[0]
604        );
605        assert!(rows[1].contains("| +25.0% | +25.0% |"), "{}", rows[1]);
606        assert!(rows[2].contains("| 0.0% | 0.0% |"), "{}", rows[2]);
607        assert!(!text.contains("-0.0%") && !text.contains("+-"), "{text}");
608        assert!(text.contains("a negative reduction means Majordomus used more tokens"));
609    }
610
611    #[test]
612    fn a_state_is_spelt_as_it_serialises_and_an_interval_at_its_declared_level() {
613        use crate::economics::model::{
614            EconomicsFreshness, EconomicsInterval, EconomicsPairCounts, EconomicsSuiteView,
615        };
616        let mut s = summary(
617            true,
618            vec![EconomicsPair {
619                status: EconomicsPairStatus::ControlFailed,
620                ..pair(&[])
621            }],
622        );
623        s.metrics[0].interval = Some(EconomicsInterval {
624            level_bp: 9750,
625            low: -0.05,
626            high: 0.3,
627            method: "cluster bootstrap over tasks".into(),
628            resamples: 2000,
629            seed: 7,
630        });
631        s.suites.push(EconomicsSuiteView {
632            id: "pilot".into(),
633            kind: "live".into(),
634            version: 1,
635            title: "Pilot".into(),
636            model: None,
637            freshness: EconomicsFreshness::NoEvidence,
638            freshness_detail: None,
639            runs: 0,
640            pairs: EconomicsPairCounts::default(),
641            revisions: vec![],
642            harnesses: vec![],
643            models_reported: vec![],
644        });
645        let text = markdown(&s);
646        assert!(text.contains("| pilot | live | no evidence |"), "{text}");
647        assert!(text.contains("| control failed |"), "{text}");
648        assert!(text.contains("| 97.5%: -5.0% to +30.0% |"), "{text}");
649        assert!(
650            text.contains("  - interval: cluster bootstrap over tasks; 2000 resamples, seed 7.")
651        );
652        for debug in ["noevidence", "ControlFailed", "NoEvidence"] {
653            assert!(!text.contains(debug), "{debug} in {text}");
654        }
655    }
656
657    #[test]
658    fn records_that_could_not_be_read_are_listed_even_without_a_methodology() {
659        let mut s = summary(false, vec![]);
660        s.diagnostics = vec!["runs/pilot/r1.json: expected value at line 1".into()];
661        let text = markdown(&s);
662        assert!(text.contains("## Records that could not be read"));
663        assert!(text.contains("- runs/pilot/r1.json: expected value at line 1"));
664        s.diagnostics.clear();
665        assert!(
666            !markdown(&s).contains("could not be read"),
667            "no problem, no section"
668        );
669    }
670
671    #[test]
672    fn the_limitations_name_the_shell_only_bootstrap_and_the_recorded_revision() {
673        let text = markdown(&summary(true, vec![]));
674        let limits = &text[text.find("## Limitations").expect("a limitations section")..];
675        assert!(limits.contains("`majordomus worktree`"), "{limits}");
676        assert!(limits.contains("measured as such"), "{limits}");
677        assert!(
678            limits.contains("at the revision it recorded, not at the current one"),
679            "{limits}"
680        );
681    }
682
683    #[test]
684    fn the_metrics_table_spells_the_value_as_value_does() {
685        let text = markdown(&summary(true, vec![]));
686        assert!(text.contains(
687            "| `effective_token_reduction` | +20.0% | preliminary | derived from observed | 3 | — |"
688        ));
689        assert!(!text.contains("## Pairs"));
690    }
691}