Skip to content

Replace pyreverse PNG UML with auto-generated mermaid overview (humans + agents) #963

Description

@drbenvincent

Replace pyreverse PNG UML with auto-generated mermaid overview (humans + agents)

Summary

Replace the pyreverse-generated PNG UML diagrams with a curated, auto-generated mermaid class diagram embedded in ARCHITECTURE.md. Mermaid is plain text (useful for AI agents reading the repo) and renders as a clean diagram on GitHub and in Sphinx (useful for humans). This supersedes #814 and removes the graphviz/pylint dependency and the weekly auto-PR workflow.

Motivation

  • The current pyreverse -o png diagrams (docs/source/_static/classes.png, packages.png) are near-useless: every class is dumped with full attribute lists and long method signatures.
  • Use pyreverse --no-signatures for UML diagrams once pylint 4.1 is released #814 is blocked on an unreleased pylint (>=4.1) just to get --no-signatures. That has been pending for months with no release in sight.
  • PNG images cannot be read by AI agents at all, so they do nothing for the goal ARCHITECTURE.md serves (orienting agents to repo structure).
  • The PNGs are only referenced in CONTRIBUTING.md; they are not part of the rendered Sphinx docs.

Feasibility findings (already verified locally)

  • The installed pylint 4.0.4 pyreverse can already emit mermaid via -o mmd, so mermaid output needs no new tooling and no unreleased pylint.
  • Raw pyreverse -o mmd is 714 lines of attribute/signature bloat — the exact problem we want to avoid.
  • pyreverse -o mmd -k (class names only) is 136 lines, no signatures (sidesteps Use pyreverse --no-signatures for UML diagrams once pylint 4.1 is released #814), but it is a flat, alphabetical, ungrouped dump and it drops every BaseExperiment -> subclass inheritance arrow (the most important hierarchy in the repo). So zero-code pyreverse-mermaid removes the bloat but is still incomplete and ungrouped.
  • pyreverse offers no "member names without signatures" mode: it is all-or-nothing (-k strips members entirely; default dumps full signatures; --no-signatures is the unreleased pylint 4.1 feature from Use pyreverse --no-signatures for UML diagrams once pylint 4.1 is released #814). The desired output — attribute and method names but no parameter/return signatures — therefore requires a small custom generator regardless.
  • The pyreverse packages mermaid diagram is broken: every node renders as class py (dotted-name bug). Useless.
  • The repo already has AST introspection that does not import causalpy (scripts/_ast_introspection.py), and a drift-check pattern that keeps ARCHITECTURE.md honest (scripts/check_architecture_inventory.py, wired into prek via make check-architecture). A curated mermaid generator should reuse this infrastructure.

Proposed approach

Add a small AST-based generator (reusing scripts/_ast_introspection.py) that emits a curated, subsystem-grouped mermaid class diagram, and embed it directly in ARCHITECTURE.md inside a fenced ```mermaid block.

Each class shows its attribute names and method names, but never full function signatures — methods render as bare name() with no parameters or return types. This is the key difference from raw pyreverse -o png: we keep the member inventory (which orients both humans and agents) while dropping the signature bloat that made the PNGs unreadable. AST gives us member names directly (function defs, annotated/plain assignments) without resolving parameters or types.

Target diagram shape (grouped by subsystem; attribute + method names, no signatures):

  • BaseExperiment and its 12 subclasses (experiment inventory)
  • ModelAdapter -> PyMCModelAdapter / SklearnModelAdapter, plus the BaseExperiment *-- ModelAdapter (_model_backend) composition
  • PyMCModel and its subclasses (the Bayesian model backend)
  • Step protocol -> EstimateEffect / SensitivityAnalysis / GenerateReport
  • (optional) Check and its subclasses

Example output (prototype):

classDiagram
    class BaseExperiment {
        +data
        +idata
        +labels
        +model
        +supports_bayes
        +supports_ols
        +effect_summary()
        +fit()
        +generate_report()
        +get_plot_data()
        +plot()
        +print_coefficients()
    }
    class ModelAdapter {
        +fit()
        +predict()
        +score()
    }
    class PyMCModel {
        +idata
        +fit()
        +predict()
        +score()
        +calculate_impact()
    }
    class Step {
        +run()
    }

    BaseExperiment <|-- InterruptedTimeSeries
    BaseExperiment <|-- PiecewiseITS
    BaseExperiment <|-- DifferenceInDifferences
    BaseExperiment <|-- StaggeredDifferenceInDifferences
    BaseExperiment <|-- SyntheticControl
    BaseExperiment <|-- SyntheticDifferenceInDifferences
    BaseExperiment <|-- RegressionDiscontinuity
    BaseExperiment <|-- RegressionKink
    BaseExperiment <|-- PrePostNEGD
    BaseExperiment <|-- InversePropensityWeighting
    BaseExperiment <|-- InstrumentalVariable
    BaseExperiment <|-- PanelRegression

    ModelAdapter <|-- PyMCModelAdapter
    ModelAdapter <|-- SklearnModelAdapter
    BaseExperiment "1" *-- "1" ModelAdapter : _model_backend

    PyMCModel <|-- LinearRegression
    PyMCModel <|-- WeightedSumFitter
    PyMCModel <|-- SoftmaxWeightedSumFitter
    PyMCModel <|-- SyntheticDifferenceInDifferencesWeightFitter
    PyMCModel <|-- InstrumentalVariableRegression
    PyMCModel <|-- PropensityScore
    PyMCModelAdapter ..> PyMCModel : wraps

    Step <|.. EstimateEffect
    Step <|.. SensitivityAnalysis
    Step <|.. GenerateReport
Loading

(Members shown as bare names — e.g. effect_summary(), not effect_summary() EffectSummary — so the diagram stays readable. Subclasses carry their own members the same way; omitted here for brevity.)

Keeping it current

Follow the existing inventory pattern: the generator supports --check (fail if the embedded diagram drifts from the code) and --write/--print (regenerate). Wire --check into prek alongside check-architecture, and add a make target to regenerate. This replaces the weekly cron auto-PR with a deterministic check that runs on every PR.

Decisions confirmed by maintainer

Tasks

  • Add scripts/generate_uml_mermaid.py (or extend check_architecture_inventory.py) that builds the curated mermaid from AST via scripts/_ast_introspection.py, with --check / --write / --print modes. It must emit attribute and method names only (strip parameters and return annotations — render methods as name()).
  • Decide on a marker convention in ARCHITECTURE.md (e.g. <!-- BEGIN AUTO-UML --> / <!-- END AUTO-UML -->) to delimit the auto-generated mermaid block.
  • Embed the generated mermaid block in ARCHITECTURE.md.
  • Update CONTRIBUTING.md "Overview of code structure": remove the classes.png / packages.png embeds and make uml, point to the ARCHITECTURE.md mermaid.
  • Replace the make uml Makefile target with a mermaid regenerate target (e.g. make uml -> runs the generator --write).
  • Wire --check into .pre-commit-config.yaml (prek) next to the architecture inventory check.
  • Delete .github/workflows/uml.yml.
  • Delete docs/source/_static/classes.png and docs/source/_static/packages.png.
  • Remove pylint (and confirm graphviz is no longer needed) from the relevant dependency group in pyproject.toml; regenerate environment.yml via the prek hook.
  • Confirm GitHub renders the mermaid in ARCHITECTURE.md; optionally add sphinxcontrib-mermaid if the diagram should also appear in the Sphinx site.
  • Close Use pyreverse --no-signatures for UML diagrams once pylint 4.1 is released #814 as superseded, referencing this issue.

Open questions

  • Should the diagram also render in the Sphinx docs site (requires adding sphinxcontrib-mermaid to docs deps), or is GitHub rendering of ARCHITECTURE.md enough?
  • Include the Check hierarchy in the main diagram, or keep the main diagram to experiments/models/pipeline and add checks as a second smaller diagram?
  • One combined diagram vs. a few small per-subsystem diagrams (smaller diagrams render and diff more cleanly).

Reference

Metadata

Metadata

Assignees

Labels

agentsAgent related issues specifically for use by developersdevopsDevOps relateddocumentationImprovements or additions to documentation

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions