REDD MUNRO

Find out how much of your dashboard is measuring the same thing
For platform & SRE teams

Your board has 200 panels. How many independent signals is that?

A forty-panel board driven by four underlying system states gives its owner the feeling of forty-fold coverage and the reality of four.

Nobody buys the forty panels. They accumulate — one per incident, one per migration, one per person who left. The count goes up and the number of things you can actually see does not, and there is no moment at which anyone is told the difference.

This measures that difference. Point it at a dashboard export and it reports how much independent information your telemetry carries, which panels restate one another, and which are safe to archive — with the evidence for each, and the reasons you should not.

  1. Redundant telemetry Panels restating one underlying quantity measured
  2. Duplicate alerting Separate rules resolving to the same state measured
  3. Overload mid-incident Four pages open, one cause, no map asserted, not measured
  4. Retention & query cost Paid per series, per month, indefinitely your prices, our arithmetic

Link three is the one everyone asserts and nobody has tested, including us. It is written down as an unscored prediction in the public repository, and it is the main thing we are looking for teams to help settle.

Runs in your browser. Your file is read in this tab and never uploaded — open the network panel and check.

What comes back

A real result on the corpus that ships with the tool: a live Prometheus instance, eleven metrics, 437 samples. Small enough to check by hand, which is the point of using it as the example.

The board

11panels
437samples of history

Presented as eleven independent views of the system.

The audit

5.6independent signals
49.3%restates something already on the board
2archive candidates
2CONFLICTS — one behind a paging alert
Aevidence grade — enough history to act on
# the audit figures
redd run   prometheus_infra.csv --basis differenced --ordered

# the archive candidates, and the conflicts against your rules
redd prune prometheus_infra.csv --basis differenced --ordered \
     --refs ./monitoring --worksheet ws.csv

Eleven panels is a small board. The ratio is what travels: half of a production Prometheus instance was restating the other half, on a set small enough that its owners could reasonably believe they knew it.

The row that explains the whole product

Two of those eleven metrics are statistically interchangeable. Only one of them is safe to touch.

methodGET_status200vsrequest_rate_total r = 0.99987
Not similar. The same numbers, on two separate panels, alerting separately.

request_rate_total carries 0.0011 unique variance. On the arithmetic alone it is a restatement of the other column and the engine marks it ARCHIVE. Point the reference scan at your alert rules and the worksheet leads with this instead:

** CONFLICT: engine says ARCHIVE, but this metric is
   behind a PAGING alert (RequestRateCollapse) **

Both facts are true, and only one of them is in the CSV. Archive it on the arithmetic alone and the next outage pages nobody. That row is sorted to the top of every worksheet, paging alerts first, because on a two-hundred-panel board the one that matters otherwise sits in the middle looking like all the others.

This is why the tool ends at a worksheet rather than a delete button. The arithmetic is the easy half.

An open call for ten teams — not a trial you sign up for

Everything above was measured on public data. Nobody has run this on a board they would be paged for.

So there is nothing to buy here and no demo to book. There is an open call for ten teams to run the same audit on their own dashboards and say what came back — including nothing.

01
Nothing leaves your machine The audit on this page runs in your tab via WebAssembly. The CLI is numpy and a CSV file. No agent, no account, no upload — and a test asserts the code cannot open a socket. Check the network panel rather than this sentence.
02
The predictions are already written down What we expect you to do, and where we expect you to give up, is registered in ADOPTION_PREREG.md — unscored, and committed before any of you ran it. Read it first, then check whether we were right about you.
03
Results get published, nulls included Including teams who report that it found nothing, and the count of teams who never replied at all. A cohort that publishes only its successes is an advert with a sample size.

What it costs you. The example board below takes about two minutes. Your own board takes longer, and the slow part is not the audit — it is getting an export into one column per metric and one row per timestamp, which is not one click in Grafana. We predict that is where most of you will stop, and it is better said here than discovered in a reply.

What you get. Your numbers, the worksheet, and the tool — Apache 2.0, yours to keep and to fork. What we get is the first evidence that any of this survives contact with a board somebody is paged for. That is the entire trade; it is deliberately not a discount on something later.

No NDA needed to send a finding, happy to sign one if you would rather. Your team is not named in the write-up unless you ask to be.

Join the ten →

Run an audit

Export any dashboard to CSV. Columns are metrics, rows are timestamps. Nothing is uploaded.

How to use this

01Drop your export Grafana, Datadog, Prometheus — anything that produces a CSV. First row is metric names. Read in this tab, never sent anywhere.
02Pick a lens Optional. Renames the findings for your domain. It cannot change a number, only what the finding is called.
03Start with what fired Each finding names the metrics involved. Archive candidates come with the evidence for why they're safe.
Drop a CSV here
or click to choose · nothing leaves this machine
No export to hand? This one is 17 metrics × 400 days, built from four known drivers with two exact duplicates and one nonlinear pair hidden in it. The audit comes back with 4.3 — it was never told the answer.

Who this is for

Platform and SRE teams with a board nobody will prune.

If you have two hundred tiles, four pages open during one incident, and a shared understanding that nobody has deleted a panel in two years because whoever deletes the wrong one owns the next incident review — this was built for you.

If your dashboard is ten panels and you know what every one of them does, you do not need this and it will tell you so.

In your terminal, in CI, in your pipeline

The browser above proves the arithmetic. It cannot show you the part that makes archiving safe.

pip install redd-munro

Apache 2.0 · numpy is the only dependency · nothing is uploaded from the CLI either, and there is a test asserting the code cannot open a socket.

01Audit Declare the basis and row order. The engine computes every view and never guesses which one you meant.
02Worksheet One row per metric, evidence pre-filled, and four columns only a person can answer.
03Reference scan Reads your Prometheus rules and Grafana JSON to fill in what a machine can — and nothing else.
04Shadow board A copy where the candidates do not resolve. See what breaks before it does.
05Route Collector config that moves redundant series to cheap storage. Never deletes, never applies itself.
06Record A run per week. The report leads with the window where a "redundant" pair came apart.

Step 03 is what produced the CONFLICT above. It answers two of the worksheet's four attestation columns — monitors and dashboards. SLOs and runbooks it cannot read, and it says not found in N scanned sources rather than "unreferenced", because a monitor in a repo it was never pointed at is invisible to it.

What it checks

Every check runs on every file — and the ones that did not fire get printed too, along with the one we have not built yet. You can tell what a tool actually covers by what it admits it doesn't.

01
Everything spiked, so everything correlated During an incident every metric moves at once, and raw correlation calls them all related. Both the raw and the change-over-time view are computed and shown, so a shared timeline doesn't get read as a shared cause.
02
The same number on two tiles A rate and its complement. A count and its percentage. Bytes and megabytes. Pairs matching at r ≥ 0.999 are one measurement displayed twice — and you get told which one to keep.
03
Totals that are just their parts A request total that is exactly the per-status counters added up. Archive the total, keep the parts — never the reverse, because every member of that family looks individually redundant and dropping them all loses the lot.
04
Worst-of and max() panels A cluster-health tile taking the max across nodes is fully determined by its inputs, yet naive scoring ranks it your most important signal — because which node is worst keeps changing.
05
Related, but your correlation check says no Two metrics sitting at r ≈ 0.03 that are strongly dependent anyway: a threshold effect, a saturation curve, a queue that only backs up past 80%. Measured against a shuffled null so the estimator's own noise isn't reported as a finding.
06
When normalising changes the answer Divide by request rate and two panels suddenly look identical. That's either a real duplicate you'd been missing, or an artefact you just created. You get both readings and how to tell them apart — it will not guess for you.

"Safe to archive" is a claim, so it has to be checked

The real reason nobody prunes a dashboard is that whoever deletes the wrong panel owns the next incident review. So nothing here archives anything on its own.

It can prove one thing That a metric carries no variation the others don't already carry. That is arithmetic, and it is checkable.
It cannot see the rest That the same metric is the sole condition on a paging rule, an SLO error budget, or a compliance export. None of that is in a CSV of values, and the tool says so rather than guessing.
So it hands you a worksheet Every candidate with the evidence for why it is listed, and columns only you can fill: referenced by monitors, SLOs, runbooks, last queried. Completing that is the safety check. It downloads with every audit, next to a summary you can paste into the change request — one that says on its own face that it is not a sign-off.

One guard worth knowing about: when a total and its parts are all on the board, every one of them looks individually redundant. An unguarded tool offers you the whole family. This one protects the parts and offers only the total.

We wrote down what we expected to find. Then we checked.

A tool only ever pointed at data whose structure nobody knows can never be caught being wrong. So the predictions go on disk before the data is pulled, and get scored afterwards — misses included. Several of the most useful results were our own bugs.

DashboardShapeResultWhat it found
NYC COVID daily counts53 × 455 5.1 signalsRaw view says 1.6 — during a pandemic every metric rides the same wave
ACT air quality12 × 1,094 4.7 signalsNO₂ and CO at r = 0.03 but 177× their Gaussian-implied dependence
Prometheus infrastructure11 × 437 5.6 signalsstatus=200 and request_rate identical at r = 0.99987
FDIC bank call reports39 × 1,915 2.5 → 14.8Raw totals said 2.5. Normalising per-unit revealed 14.8 — size was hiding everything
Worth knowing what that discipline catches. Adding the totals check exposed a bug we had been shipping: when a total and its parts are all on the board, every one of them looks individually redundant — so the tool offered all six members of one family for deletion at once. Take that advice and you lose the whole thing. It is guarded now, and the episode is written up in the public failure log rather than quietly patched.

Engine boundaries

What this cannot do, in one place, stated rather than discovered. Each of these is either a published miss or a limit we measured and could not remove.

Rolling averages A metric plotted against its own rolling average is not detected. Two designs were built and withdrawn on evidence — one flagged independent random walks, the other produced 476 false positives while missing the confirmed real case. It renders as an unchecked circle in every report rather than being left off the list.
Subset averages A column that is the mean of some of the others is not found. The Open LLM Leaderboard's Average column matched at r = 1.00000000 on 541 of 541 models and nothing flagged it. Exact subset sums are detected; means are not.
Counter data is judged more leniently The nonlinear check computes one noise floor per dataset. Integer counters carry far less entropy than continuous gauges, so tied columns are systematically less likely to be flagged in the same audit. Under-detection, not over-detection — the safer direction, and measured at a hundredfold spread across fixtures.
Cross-sections need normalising On data where rows are entities rather than timestamps, size drives every correlation and everything looks related. Ratio-normalise before running. The tool reports both readings and the test that separates them; it will not pick for you.
A metric held flat by a control loop looks archivable The sharpest one here, because the arithmetic is right and the advice is still dangerous. A channel an autoscaler or GC loop is actively pinning becomes a near-deterministic function of the load and the effort absorbing it — so it carries no unique variance and is offered for archiving. It is also the channel that moves first when that loop stops working. Measured on a simulated controller: unique variance falls 0.90 → 0.0000 as the loop tightens. Not detected, and no flag for it.
Nothing here is causal Two metrics in a cluster may share a driver, or one may cause the other, or both may be measuring an instrument rather than the world. The engine sees covariation and says so. It has no access to your architecture, your incident history, or which panel someone added after a bad night.
It does not know what your metrics mean A statistically redundant column may be worth keeping for contractual, regulatory or diagnostic reasons that appear nowhere in a CSV of values. The reference scan reads Prometheus rules and Grafana JSON; SLO definitions and runbooks it cannot read at all.

The full record, including the two pre-registered predictions that failed and the cycle whose misses disproved the previous cycle's diagnosis, is in the repository — six studies, four with published misses.

About the engine & author

One question: how many distinct things is this dashboard actually measuring? A forty-panel board driven by four underlying system states gives its owner the feeling of forty-fold coverage and the reality of four. That gap is where duplicate alerts come from, and it is what this measures.

It was built by someone who got tired of dashboards nobody trusted and panels nobody would delete, and who wanted the argument settled with evidence instead of opinion.

How we know it works

Before each dataset is pulled, what we expect to find gets written down and committed. A tool only ever pointed at data whose structure nobody knows can never be caught being wrong — so we make it possible to catch. Every dataset in REAL_DASHBOARDS.md carries its predictions and the score, misses included. Several of the most valuable results were misses, because they turned out to be bugs in this tool rather than bad guesses about the data.

You do not have to take that on trust. The source, the eight pre-registration documents — six scored, two still open — and every corpus they were scored against are public — read them on GitHub. "We wrote the predictions first" is an empty claim on a marketing page; in commit history it is checkable, which is the only reason it is worth saying.

Structural invariants

  • It never guesses how to read your data. Raw, change-over-time, and per-unit views are all computed. Which one the headline reports is something you declare — because a tool that guesses wrong hands you a confident wrong answer, and that is the exact failure this exists to catch.
  • The engine is blind to meaning. It emits mathematical states. Domain lenses rename them in this interface and cannot reach the mathematics.
  • Two readings, never a verdict. When normalising makes two panels look identical, that is either a duplicate you had missed or an artefact the normalising created. Both turned up in the same dataset looking exactly alike. So you get both readings and the test that separates them — you know your system, the arithmetic doesn't.
  • Checks that did not fire are still shown. Anyone can print a correlation matrix and a big number. The difference is the list of ways this one was stopped from lying to you — including the check we have not built, which is on the page rather than left off it.

What it does not do

Seven boundaries, each either a published miss or a limit that was measured and could not be removed, are set out in full under engine boundaries above — rolling averages, subset means, counter-data leniency, cross-sections, causality, and semantics.

Where the name comes from

Redd Munro is not a person. Both halves are Scots, and both describe the tool.

Redd means to clear out and set in order — you redd up a room before you can see what is in it. A Munro is a Scottish peak over 3,000 feet, and the genuinely hard part of Sir Hugh Munro's 1891 tables was never the measuring. It was deciding which summits count as separate mountains and which are just subsidiary tops of the same one. People have argued about it ever since.

That is this tool in one sentence. Not every summit is a separate mountain, and not every panel is a separate signal.

Author

Built by Shaun Cooper, independent systems theorist. The engine grew out of a research programme that spent most of its effort trying to break its own measurements, and catalogued the distinct ways a comparison like this can produce a publishable-looking wrong answer.

Runs entirely in your browser via Pyodide. No account, no upload, no telemetry — which would be an awkward thing for this particular tool to collect.

The one question nobody can answer alone

Every "keep it just in case" argument in this industry rests on a belief that has never been tested.

The belief is that metrics which look identical come apart during incidents — that the pair sitting at r = 0.99987 all quarter diverges exactly when it matters, which is why deleting either one feels dangerous. It is the load-bearing assumption under every dashboard nobody prunes.

It has never been tested. Not here, not anywhere. It is written down as prediction H1 in the public repository, deliberately unscored, because settling it needs something no public dataset contains: a window somebody labelled incident because their service was genuinely broken.

If you have that window, you can settle a question this whole industry has been assuming the answer to.

shaun@reddmunro.com

And if you ran it and it found nothing, that is worth an email too — a tool that only hears from the cases where it worked learns nothing.