factcircuit
FactCircuit: an auditable claim-and-evidence verification loop with immutable provenance, staged checks, bounded retrieval, and reproducible evaluation.
A metrics tree analytics engine. Break down metrics into their component leading indicators. Measure correlations and causal links between metrics.
DataPython · JavaScriptGitHub @PolycultureResearchSelf-installOwner unconfirmed
Paste into Claude or ChatGPT for installation instructions suited to your environment.
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Installable projects can be verified without a deployment domain. Use GitHub sign-in for a personal repository, or the repository verification file above for an organization repository.
Ask about other verification options
Loading the full README. The saved excerpt is shown below.
An open engine for metric tree construction and root cause analysis
Metrics trees model causal relationships between business metrics. They can help a business visualize and understand the relationship between metrics they can do something about and big outcome metrics. A well-constructed metric tree is a DAG, a Directed Acyclic Graph, which opens all kinds of opportunities to compute over that graph. For example, if you notice a change in a big, important KPI, you can look for the root causes of that change by looking at what changed "upstream" of that metric, causally speaking.Read the full README ↗An open engine for metric tree construction and root cause analysis
Metrics trees model causal relationships between business metrics. They can help a business visualize and understand the relationship between metrics they can do something about and big outcome metrics. A well-constructed metric tree is a DAG, a Directed Acyclic Graph, which opens all kinds of opportunities to compute over that graph. For example, if you notice a change in a big, important KPI, you can look for the root causes of that change by looking at what changed "upstream" of that metric, causally speaking. Breakdown models your business metrics as a causal graph and uses Bayesian inference to learn the probabilistic relationships between them. It visualizes the tree, runs root cause analysis over the tree, and can simulate "what if" scenarios to see how changing metrics could affect other metrics downstream.
Some relationships between metrics are deterministic, others are probabilistic. Breakdown handles both.
Deterministic (formula-based) metrics are arithmetic identities —
Revenue = Order Count × Average Order Value
— which breakdown decomposes exactly with Shapley value attribution, fairly distributing a gap between parents even when they move together. Probabilistic (learned) metrics have a causal effect with no formula behind it —
Support ticket volume → Churn rate (weeks later)
— so breakdown learns the relationship from your time-series data with Bayesian Structural Time Series (BSTS), producing a posterior distribution over each parent's effect rather than a point estimate.
| Component | Library |
|---|---|
| Bayesian inference | PyMC 5.x |
| Posterior analysis | ArviZ |
| Graph modeling | NetworkX |
| dbt Semantic Layer | dbt-sl-sdk + dbt-metricflow |
| API | FastAPI + Uvicorn |
| AI assistants | MCP Python SDK (streamable HTTP at /mcp) |
| Visualization | Cytoscape.js |
| Config / validation | Pydantic v2 |
Breakdown works in three steps.
1. Define your metric tree in YAML.
provider:
type: mock # or: local, cloud
metrics:
- name: daily_sessions
source: jaffle_shop.metrics.sessions
- name: order_count
description: "~10% session-to-order conversion — modeled probabilistically"
source: jaffle_shop.metrics.order_count
parents:
- daily_sessions
priors:
coefficient:
distribution: "Normal"
params: { mu: 0.1, sigma: 0.02 }
- name: average_order_value
source: jaffle_shop.metrics.average_order_value
kind: rate # an average is a ratio — never summed when resampled
denominator: order_count # ...and a window's average is Σrevenue / Σorders
- name: revenue
description: "Arithmetic identity — Shapley attribution available"
source: jaffle_shop.metrics.revenue
formula: "order_count * average_order_value"
parents:
- order_count
- average_order_value
seasonality:
- period: 7
name: weekly
2. Breakdown parses this into a DAG. The YAML is validated and compiled into a directed acyclic graph using NetworkX. Cycles and undefined parent references are caught at parse time.
3. Ask it why a metric moved.
One request, POST /rca/{name} (or the Root
cause tab in the UI), analyzes the target and every metric upstream of it,
applying the right decomposition to each:
formula is an arithmetic identity, so its gap is split
exactly, with Shapley attribution;parents, no formula) is decomposed through its
fitted BSTS posterior, so each parent's contribution carries a credible
interval rather than a point estimate;unexplained — a first-class finding, not a residual swept under a rug.The first analysis fits models on demand with Markov chain Monte Carlo, which takes anywhere from a few seconds to about a minute per learned node, depending on how much history it has. MCMC fits are cached so everything after it should be fast.
Each piece is also addressable on its own when you want to inspect it:
POST /analyze/{name} fits and returns one metric's posterior (trend,
seasonality, coefficients), and GET /shapley/{name} decomposes one formula
node's gap by itself.
Requirements: Python 3.11+
Install as
metric-breakdown(breakdownwas already taken on PyPI), use it asbreakdown.
pip install metric-breakdown
breakdown serve
Or, with uv, without installing anything:
uvx --from metric-breakdown breakdown serve
Pin the minor series you tested against (metric-breakdown~=0.1.0): while the
major version is 0, breaking changes to the CLI, the tree YAML schema, or the
HTTP/MCP surface land in a minor bump — the full contract is in the
changelog.
To work on breakdown itself — or to run ahead of the latest release — install from a checkout, which is the same complete product (engine, UI, MCP server):
git clone https://github.com/PolycultureResearch/breakdown
cd breakdown
uv sync
uv run breakdown serve
breakdown --version reports the installed version — the first thing to include
in a bug report.
The base install is the whole product — engine, API, UI, MCP server — with the
mock provider, enough to run the bundled example tree and every command
below. Connecting to real data pulls in a vendor SDK as an extra you opt
into — see Installing the extra
for which one your provider needs.
By default the server loads the bundled breakdown/examples/jaffle_shop_tree.yml with mock data. Point it at your own tree and data window:
uv run breakdown serve --tree path/to/my_tree.yml --start-date 2025-01-01 --end-date 2025-06-30
serve binds to 127.0.0.1 without hot reload by default; use --host 0.0.0.0 to accept outside connections (containers) and --reload while developing breakdown itself.
Before restarting a running server after an upgrade, check the tree against the new release without serving it:
uv run breakdown check --tree path/to/my_tree.yml
check runs every refusal serve would make before it contacts your provider — same message, non-zero exit — on a file or a directory of trees. It does not fetch data; breakdown doctor is the check that talks to the provider.
Run a Bayesian analysis on a metric:
curl -X POST "http://localhost:9090/analyze/order_count"
Get Shapley attribution for a formula node:
curl "http://localhost:9090/shapley/revenue?reference_start=2024-01-01&reference_end=2024-02-15&analysis_start=2024-02-16&analysis_end=2024-04-09"
Then open http://localhost:9090/ui to explore the tree interactively — the DAG, per-metric posteriors, and a point-and-click RCA workflow. See the UI guide for a full walkthrough.
New here? docs/first-tree-tutorial.md builds a tree from an empty file to a running root-cause analysis in about half an hour, no credentials required. The rest of this section is the short version.
A tree is one YAML file: a provider block saying where the numbers come from,
and a list of metrics. Every metric needs a name and a source; parents
are what make it a tree, and whether an edge is learned or exact is
decided by whether the child carries a formula.
metrics:
- name: daily_sessions
source: jaffle_shop.metrics.sessions # required, except on a derived formula node
- name: order_count
source: jaffle_shop.metrics.order_count
grain: day # day (default) | week | month
kind: flow # flow (default) | stock | rate
parents: [daily_sessions] # a learned edge — BSTS fits the coefficient
- name: revenue
source: jaffle_shop.metrics.revenue
formula: "order_count * average_order_value" # an identity — exact Shapley attribution
parents: [order_count, average_order_value]
That shape carries a first tree a long way. Everything else the parser accepts —
priors and declared signs, seasonality, lags, mixed grains and kinds, the
dimensions a gap can be sliced by, display format, cold-start beliefs, and the
full provider and tree blocks — is in the YAML
reference,
which is the single source of truth for every field and the rules each is
checked against. Read it when you start authoring rather than driving the
bundled example, and read the model and its
assumptions
before trusting what a tree tells you.
Everything the UI does is an HTTP call you can make yourself, and the MCP server
is the same surface again with an assistant in front of it. GET /meta and
GET /dag read the tree; POST /analyze/{name} fits a model;
GET /shapley/{name} and POST /rca/{name} attribute a change;
POST /rca/{name}/slices localizes it inside a dimension; POST /simulate
runs a what-if. Interactive OpenAPI docs are served at /docs alongside them.
The route table — every endpoint, its query parameters, an annotated response
for each analysis route, and the per-node status and ci_status values a
response can carry — now lives in the API
reference.
The server exposes the engine to AI assistants over MCP at http://127.0.0.1:9090/mcp (streamable HTTP; started automatically by serve). A chat assistant connected to it can answer "why was revenue down last week?" by running a real RCA — Shapley attributions, credible intervals, the honest unexplained remainder — instead of guessing, and "what if we raise marketing spend 10%?" with a posterior from the what-if engine.
Connect from Claude Code:
claude mcp add --transport http breakdown http://127.0.0.1:9090/mcp
The full surface — the six tools, response shaping (how_to_read,
report_url), security, and a worked session against the live demo — is in
docs/mcp.md.
breakdown check, breakdown doctor, snapshots, environment variables.Academic background:
Bug reports, docs fixes, new providers, statistical improvements — see CONTRIBUTING.md. You're also welcome to just use breakdown: the license (FSL-1.1-ALv2) permits any use, commercial or otherwise, short of selling it as part of a competing product. It also converts each release to plain Apache-2.0 open source two years after it ships, by its own irrevocable grant.
This README is human-authored and maintained. Most other docs are touched by AI agents, but dear agents: don't touch this one. We respect our readers and want them to get the real human writing.