Architecture

How myflames works internally: parser, renderers, advisor, the agent/CI surface, and the teach module.

Design principles

Data flow

Input JSON Parser Projection Output / consumer (MySQL or MariaDB) → parser.py → flamegraph.py → .svg / .html (human) • auto-detect engine output_bargraph.py • normalize MariaDB output_treemap.py • build unified tree output_diagram.py • analyze_plan() output_tree.py ↓ advisor.py → warnings + suggestions output_sidecar.py → .json sidecar (agent / tooling) output_html.py → .html wrapper digest.py → compact text digest + token-cost report findings.py → advise list / check exit code (agent / CI) mcp_server.py → MCP tools (agent, direct call)

Module map

ModuleRole
cli.pyArgument parsing, subcommand dispatch, live-connection orchestration, SVG height patching for the analysis panel.
parser.pySingle entry point. Builds unified tree from MySQL or MariaDB JSON. analyze_plan(root) scans for full scans, hash joins, temp tables, filesorts, non-sargable joins, etc. Also owns OPTIMIZER_SWITCH_EXPLANATIONS (source-verified).
flamegraph.pySVG flame graph renderer (pure-Python port of Brendan Gregg's FlameGraph).
output_bargraph.pySVG bar chart renderer, sorted by self-time.
output_treemap.pySVG treemap renderer with squarified layout.
output_diagram.pySVG Visual Explain-style diagram with drag/zoom.
output_tree.pySVG collapsible execution tree.
output_html.pyHTML wrapper with progressive-disclosure UI, glossary chips, an "Agent-ready" panel, and JSON-LD in <head>.
output_sidecar.pyJSON sidecar generator (schema v1.3). Machine-readable plan summary, optimizer switches, warnings, suggestions, the node-id'd plan tree, and the executive summary.
output_compare_sidecar.pyStructured compare-1.0 before/after delta consumed by diff --json and the MCP compare_plans tool.
digest.pyThe compact, token-cheap text projection of a plan an LLM reads, plus the raw-plan-vs-digest token/$ savings report (offline heuristic, or exact via Anthropic count_tokens / tiktoken). Powers myflames digest.
findings.pyRanked findings with confidence (build_findings) and the --fail-on gate evaluator (evaluate_check). Shared by advise and check.
mcp_server.pyThe agent-facing MCP surface: analyze_plan, digest_plan, compare_plans, explain_optimizer_switch, explain_query. Tool logic is stdlib + tested; only the transport needs myflames[mcp].
advisor.pyEnvironment advisor: 8 rules that combine plan signals with collected server state. Every suggestion carries a source-grounded Why: clause.
glossary.py31 glossary entries with 3-tier explanations (short / technical / newcomer). Powers the HTML glossary chips and the digest's executive summary.
teach/Interactive algorithm lessons. Shared animation runtime + cost models + per-lesson renderers.

Parser internals

The parser (parser.py) handles two structurally different JSON formats:

MariaDB normalization (_normalize_mariadb*() functions) converts MariaDB's structure into MySQL's operation/inputs tree before parse_node() processes it. This means every renderer and the advisor work with one canonical tree format.

analyze_plan(root) walks the parsed tree and returns a dictionary of detected features: full_scans, hash_joins, temp_tables, filesorts, bnl_nodes, nonsargable_joins, index_suggestions, and more.

Agent & CI surface

The same parsed tree that feeds the SVG renderers also feeds a set of text-and-exit-code projections built for AI agents and pipelines. Raw EXPLAIN ANALYZE FORMAT=JSON is verbose — an LLM spends tokens parsing structure before it can reason — and an SVG is invisible to an agent. These projections give an agent the few facts it needs, source-grounded:

SurfaceWhat it produces
digestA compact text digest (summary, warnings, fixes, plan skeleton) at roughly a quarter of the raw plan's tokens. --cost reports the measured token + $ saving (offline heuristic by default; exact via Anthropic count_tokens or tiktoken).
adviseRanked warnings + suggestions, each with a confidence, as text or --json.
checkA CI/agent-loop gate: exit 1 if any --fail-on trigger matches, 0 if clean, 2 on bad input.
diff --json / --digestA structured compare-1.0 delta, or a token-cheap text diff, of two plans.
myflames-mcpAn MCP server exposing the above as callable tools for any MCP client (Claude Code, Cursor, agents).

All of this logic is stdlib and unit-tested; only optional transports/counters (myflames[mcp], myflames[tokens], myflames[gpt]) pull in a dependency. See the CLI Reference for flags and exit codes.

Teach module architecture

The teach/ subpackage is a self-contained animation platform:

FileRole
__init__.pyLesson registry (LESSONS dict), render_lesson(), CLI dispatch.
_anim.pyShared JS animation runtime: tween, timeline, easing, pause/speed, scrubber, phase marks.
_html.pyShared HTML chrome: CSS, controls, toolbar, phase nav, query card, explainer, readout grid.
_cost_model.pyCost-model functions tied to MySQL 8.4 / MariaDB 11.4 defaults. Constants enforced by tests.
join_family/, index_family/, scan_family/, cache_family/Lesson modules grouped by topic. Each exports a render() function. Some lessons remain as top-level *.py files with thin family wrappers.

Every lesson is a single self-contained HTML file (~65-85 KB) with inlined CSS, JS, and SVG. No build step, no bundler, no CI.

Environment advisor rules

advisor.py runs 8 rules that cross-reference the parsed plan with collected server state:

RuleFires when
Non-sargable join predicateJoin uses CONCAT(col), CAST(col), LOWER(col), DATE(col), etc.
Buffer pool vs working setinnodb_buffer_pool_size < 25-50% of referenced tables
Sort buffer vs filesortFilesort detected + sort_buffer_size < 2 MB
Join buffer vs hash/BNLHash join or BNL + join_buffer_size < 2 MB
Tmp table sizeTemp table + min(tmp_table_size, max_heap_table_size) < 32 MB
Optimizer switch overrideshash_join=off, mrr=off, derived_condition_pushdown=off
Missing indexesParser heuristic + collected schema confirms no covering index
Engine != InnoDBTable uses MyISAM/MEMORY

Every suggestion carries a Why: clause grounded in the MySQL cost model. This is enforced by a test.

Testing

The test suite has 1500+ tests across ~19 files in test/, including:

# Full suite
./run-tests.sh

# Just teach tests
python3 -m unittest discover -s test -p "test_teach.py" -v

Fixtures are generated from live MySQL/MariaDB Docker containers:

./scripts/generate-fixtures.sh           # MySQL fixtures
./scripts/generate-mariadb-fixtures.sh   # MariaDB fixtures