Architecture
How myflames works internally: parser, renderers, advisor, the agent/CI surface, and the teach module.
Design principles
- One analysis, many projections — a plan is parsed and analyzed once, then projected for whoever is asking: flame graph / bar chart / treemap / diagram / tree (for humans), a compact source-grounded digest and JSON sidecar (for AI agents), and an exit code (for CI).
- Zero external dependencies — Python 3.7+ stdlib only. No pip install of MySQL drivers, templating engines, or JS frameworks. The AI-era integrations (MCP transport, exact token counting) live behind optional extras (
myflames[mcp],myflames[tokens],myflames[gpt]); the core stays stdlib-only. - Single parser, multiple renderers — JSON is parsed once into a unified tree. Renderers never re-parse JSON.
- Offline-first output — every HTML file is self-contained. No external scripts, stylesheets, or fonts. Works in Slack DMs, email attachments, and air-gapped environments.
- MySQL + MariaDB parity — the parser auto-detects the engine and normalizes MariaDB's different JSON structure into the same internal tree.
- Source-grounded, never guessing — every advisor rule and
optimizer_switchexplanation is verified against MySQL/MariaDB server source, so the digest an agent reads is factually correct.
Data flow
Module map
| Module | Role |
|---|---|
cli.py | Argument parsing, subcommand dispatch, live-connection orchestration, SVG height patching for the analysis panel. |
parser.py | Single 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.py | SVG flame graph renderer (pure-Python port of Brendan Gregg's FlameGraph). |
output_bargraph.py | SVG bar chart renderer, sorted by self-time. |
output_treemap.py | SVG treemap renderer with squarified layout. |
output_diagram.py | SVG Visual Explain-style diagram with drag/zoom. |
output_tree.py | SVG collapsible execution tree. |
output_html.py | HTML wrapper with progressive-disclosure UI, glossary chips, an "Agent-ready" panel, and JSON-LD in <head>. |
output_sidecar.py | JSON 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.py | Structured compare-1.0 before/after delta consumed by diff --json and the MCP compare_plans tool. |
digest.py | The 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.py | Ranked findings with confidence (build_findings) and the --fail-on gate evaluator (evaluate_check). Shared by advise and check. |
mcp_server.py | The 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.py | Environment advisor: 8 rules that combine plan signals with collected server state. Every suggestion carries a source-grounded Why: clause. |
glossary.py | 31 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:
- MySQL 8.4+ uses an
operation/inputs[]tree structure (format version 2). - MariaDB 10.11+ uses a
query_block/nested_loop/tablestructure.
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:
| Surface | What it produces |
|---|---|
digest | A 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). |
advise | Ranked warnings + suggestions, each with a confidence, as text or --json. |
check | A CI/agent-loop gate: exit 1 if any --fail-on trigger matches, 0 if clean, 2 on bad input. |
diff --json / --digest | A structured compare-1.0 delta, or a token-cheap text diff, of two plans. |
myflames-mcp | An 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:
| File | Role |
|---|---|
__init__.py | Lesson registry (LESSONS dict), render_lesson(), CLI dispatch. |
_anim.py | Shared JS animation runtime: tween, timeline, easing, pause/speed, scrubber, phase marks. |
_html.py | Shared HTML chrome: CSS, controls, toolbar, phase nav, query card, explainer, readout grid. |
_cost_model.py | Cost-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:
| Rule | Fires when |
|---|---|
| Non-sargable join predicate | Join uses CONCAT(col), CAST(col), LOWER(col), DATE(col), etc. |
| Buffer pool vs working set | innodb_buffer_pool_size < 25-50% of referenced tables |
| Sort buffer vs filesort | Filesort detected + sort_buffer_size < 2 MB |
| Join buffer vs hash/BNL | Hash join or BNL + join_buffer_size < 2 MB |
| Tmp table size | Temp table + min(tmp_table_size, max_heap_table_size) < 32 MB |
| Optimizer switch overrides | hash_join=off, mrr=off, derived_condition_pushdown=off |
| Missing indexes | Parser heuristic + collected schema confirms no covering index |
| Engine != InnoDB | Table 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:
test_myflames.py— parser, renderers, advisor, HTML wrapper, live-connection modetest_sidecar.py/test_compare_sidecar.py— the JSON sidecar + compare delta schemastest_digest.py— the token estimator, digest builder, and raw-vs-digest savings comparison (incl. the GPT/tiktoken path)test_findings.py— ranked findings + the--fail-ongate evaluatortest_cli_contract.py— the0/1/2exit-code contract and stdout/stderr discipline for every subcommandtest_mcp_server.py— the MCP tool logic (stdlib, no transport needed)test_advisor.py,test_glossary.py,test_nonsargable.py,test_complexity.py,test_teach.py— rule correctness, glossary, and teach cost-model invariants
# 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