Charts
Memosa generates charts directly from your deal’s underlying data — the cash flows, debt schedule, rent roll, sensitivity grid, and capital stack parsed out of your Excel model — and inserts them into the relevant memo sections. The goal is never decoration. Every chart that survives into the memo has to answer a real investment-committee question, in words a reader can act on.
Supported chart types
Section titled “Supported chart types”Charts render two ways. Most types are drawn server-side with matplotlib for the PDF export and with Recharts in the live Canvas editor. The one exception is the 3D capital stack, which has its own dedicated isometric renderer.
The matplotlib dispatch covers these types : bar, horizontal bar, line, pie, donut, stacked bar, scatter, area, waterfall, stacked area, heatmap (used for sensitivity grids), combo (bar-plus-line dual-axis), horizontal stacked bar, bubble, gantt (a horizontal timeline), tornado (paired downside/upside sensitivity), comparison (a side-by-side), and outcome distribution (a Monte-Carlo histogram with P5/P50/P95 markers).
Separately, capital_stack_3d — the isometric, layered capital-stack diagram — bypasses matplotlib entirely and renders through its own SVG path.
Two chart types worth knowing
Section titled “Two chart types worth knowing”- The NOI / cash-flow waterfall shows how a starting figure bridges to an ending figure through a sequence of positive and negative steps — for example, how gross income walks down to net operating income through vacancy, expenses, and reserves. The connectors step with the running cumulative, so cascading negatives read as a clean staircase rather than floating lines.
- The 3D capital stack renders the deal’s financing layers — senior debt, mezzanine, equity, and so on — as a stacked isometric block, with each layer’s share of total capitalization and an optional source attribution line beneath each layer’s name.
Every chart must earn its place
Section titled “Every chart must earn its place”A chart only reaches the memo if it carries two things that make it useful to a reader:
- an insight caption — a sentence stating what the chart actually shows, not just what it is; and
- a decision question — the IC question the chart helps answer (for example, “Is the debt safe at this leverage?”).
A boundary validator runs at insertion time and drops any chart that’s missing either one. Specifically, a chart is discarded if its insight caption is shorter than 10 characters or its decision question is shorter than 5 characters . The same validator also rejects any chart whose data fails schema validation. These are the reader-usefulness invariants: a chart with data but no stated insight or decision purpose is treated as noise and removed.
One question per section: the distinctness filter
Section titled “One question per section: the distinctness filter”Two charts that answer the same decision question in one section are redundant for a reader. So within each section, Memosa applies a distinctness filter: it keeps the highest-ranked chart for a given decision question and drops later candidates that duplicate it (the comparison is case-insensitive and whitespace-tolerant).
There is one deliberate exception. A small set of charts is tied to critical IC decision questions — recession defensibility, debt safety, capital structure, downside protection. If one of those critical charts would otherwise be dropped as a duplicate, it is protected and kept, because its absence would leave a critical question with no chart to answer it.
Beyond distinctness, sections also enforce a per-family cap so a section never carries several near-identical visualizations of the same underlying figure, and an overall cap on how many charts a single section can hold.
How charts get inserted
Section titled “How charts get inserted”Chart suggestions are computed at synthesis time, once the section content is finalized, from every data source the deal exposes — annual cash flows, operating-expense detail, the debt schedule, sensitivity analysis, key metrics, rent-roll data, construction draws, and capex detail. Each candidate is scored, run through the boundary validator and the distinctness and family caps above, and the survivors are injected into the section as chart nodes the editor and the PDF exporter both know how to render.
Decision coverage at the memo level
Section titled “Decision coverage at the memo level”Individual charts answer individual questions; the memo as a whole is also scored on whether it answers the canonical IC decision questions. Memosa rolls every inserted chart’s decision question up against a fixed catalog of canonical questions and produces a memo-level decision-coverage grade. That grade feeds the document readiness score — see quality and readiness for how it surfaces and what to do when a critical question is left uncovered.
Sources
Section titled “Sources”src/canvas/services/chart_renderer.py— the server-side matplotlib dispatch table (the full supported-type set) and thecapital_stack_3ddirect-SVG path.src/langchain/workflows/tools/synthesis/chart_inserter.py— the P32 boundary validator (_MIN_INSIGHT_CAPTION_LEN = 10,_MIN_DECISION_QUESTION_LEN = 5, schema validation), the per-section decision-question distinctness filter with critical-chart protection, the per-family cap, and the synthesis-time insertion path.memory/chart_decision_coverage.md— the canonical IC decision-question catalog, critical vs. important tiers, and the memo-level coverage rollup that feeds readiness.memory/chart_waterfall_invariants.md— the waterfall type-string and connector-cumulative invariants shared across the Python and TypeScript renderers.memory/capital_stack_3d_invariants.md— the dual-renderer geometry contract for the 3D capital stack.