mpl-plot-report 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. mpl_plot_report-0.1.0/.gitignore +1 -0
  2. mpl_plot_report-0.1.0/.python-version +1 -0
  3. mpl_plot_report-0.1.0/AGENTS.md +182 -0
  4. mpl_plot_report-0.1.0/LICENSE +21 -0
  5. mpl_plot_report-0.1.0/PKG-INFO +123 -0
  6. mpl_plot_report-0.1.0/README.md +111 -0
  7. mpl_plot_report-0.1.0/agent.md +95 -0
  8. mpl_plot_report-0.1.0/docs/handoff.md +161 -0
  9. mpl_plot_report-0.1.0/docs/implementation.md +213 -0
  10. mpl_plot_report-0.1.0/docs/specs.md +95 -0
  11. mpl_plot_report-0.1.0/examples/ruleset_demo.py +10 -0
  12. mpl_plot_report-0.1.0/main.py +6 -0
  13. mpl_plot_report-0.1.0/mpl-plot-report-dump-20260202-0819.txt +3784 -0
  14. mpl_plot_report-0.1.0/mpl_plot_report/__init__.py +5 -0
  15. mpl_plot_report-0.1.0/mpl_plot_report/api.py +111 -0
  16. mpl_plot_report-0.1.0/mpl_plot_report/cli.py +99 -0
  17. mpl_plot_report-0.1.0/mpl_plot_report/diagnostics.py +80 -0
  18. mpl_plot_report-0.1.0/mpl_plot_report/extract.py +134 -0
  19. mpl_plot_report-0.1.0/mpl_plot_report/render_md.py +244 -0
  20. mpl_plot_report-0.1.0/mpl_plot_report/rules.py +46 -0
  21. mpl_plot_report-0.1.0/mpl_plot_report/schema.py +3 -0
  22. mpl_plot_report-0.1.0/mpl_plot_report/util.py +63 -0
  23. mpl_plot_report-0.1.0/pyproject.toml +14 -0
  24. mpl_plot_report-0.1.0/scripts/generate_fixtures.py +72 -0
  25. mpl_plot_report-0.1.0/tests/conftest.py +8 -0
  26. mpl_plot_report-0.1.0/tests/fixtures/expected/happy_path.plot.json +493 -0
  27. mpl_plot_report-0.1.0/tests/fixtures/expected/happy_path.plot.md +205 -0
  28. mpl_plot_report-0.1.0/tests/fixtures/expected/happy_path.png +0 -0
  29. mpl_plot_report-0.1.0/tests/fixtures/expected/nan_ordering.plot.json +116 -0
  30. mpl_plot_report-0.1.0/tests/fixtures/expected/nan_ordering.plot.md +214 -0
  31. mpl_plot_report-0.1.0/tests/fixtures/expected/nan_ordering.png +0 -0
  32. mpl_plot_report-0.1.0/tests/fixtures/expected/wiggles_outliers.plot.json +893 -0
  33. mpl_plot_report-0.1.0/tests/fixtures/expected/wiggles_outliers.plot.md +205 -0
  34. mpl_plot_report-0.1.0/tests/fixtures/expected/wiggles_outliers.png +0 -0
  35. mpl_plot_report-0.1.0/tests/fixtures/synthetic/crowded_and_two_peaks_hit_010.csv +68356 -0
  36. mpl_plot_report-0.1.0/tests/fixtures/synthetic/crowded_but_clean_hit_013.csv +68356 -0
  37. mpl_plot_report-0.1.0/tests/fixtures/synthetic/crowded_but_coupled_hit_009.csv +68356 -0
  38. mpl_plot_report-0.1.0/tests/test_report.py +114 -0
  39. mpl_plot_report-0.1.0/uv.lock +536 -0
@@ -0,0 +1 @@
1
+ *.pyc
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,182 @@
1
+ # Agent Guide for mpl-plot-report
2
+
3
+ This repo is a small Python package intended to export Matplotlib plot reports
4
+ as JSON + Markdown sidecars. The guidance here is for agentic coding tools.
5
+
6
+ ## Quick Start
7
+
8
+ - Python version: 3.13 (see `.python-version`).
9
+ - Dependencies are tracked in `pyproject.toml` and `uv.lock`.
10
+ - If you use `uv`, prefer `uv sync` or `uv run ...`.
11
+ - If you use `pip`, create a venv and install from `pyproject.toml`.
12
+
13
+ ## Build / Lint / Test
14
+
15
+ The project is currently very small and uses `pytest` and `black`.
16
+
17
+ ### Install / Sync
18
+
19
+ - `uv sync`
20
+ - `python -m venv .venv && . .venv/bin/activate && pip install -e .[dev]`
21
+
22
+ ### Tests
23
+
24
+ - Run all tests: `uv run pytest` or `python -m pytest`
25
+ - Run a single file: `uv run pytest tests/test_module.py`
26
+ - Run a single test: `uv run pytest tests/test_module.py::TestClass::test_name`
27
+ - Run by keyword: `uv run pytest -k "keyword"`
28
+
29
+ ### Lint / Format
30
+
31
+ - Format: `uv run black .` or `python -m black .`
32
+ - Check formatting: `uv run black --check .`
33
+
34
+ ### Build / Packaging
35
+
36
+ This repository is not yet wired with a build tool, but common options are:
37
+
38
+ - `uv build` (if using uv)
39
+ - `python -m build` (requires `build` dependency)
40
+
41
+ If you need to add a build step, update this file with the exact command.
42
+
43
+ ## Cursor / Copilot Rules
44
+
45
+ No Cursor rules found in `.cursor/rules/` or `.cursorrules`.
46
+ No Copilot instructions found in `.github/copilot-instructions.md`.
47
+
48
+ ## Code Style Guidelines
49
+
50
+ This repo is early-stage; prefer simple, explicit Python with a focus on
51
+ determinism and readability. Use the following conventions unless a file
52
+ explicitly establishes something else.
53
+
54
+ ### Formatting
55
+
56
+ - Use Black defaults (4-space indents, line length per Black).
57
+ - Keep imports sorted in logical groups (stdlib, third-party, local).
58
+ - Avoid trailing whitespace and excessive blank lines.
59
+ - Prefer short, descriptive lines over dense one-liners.
60
+
61
+ ### Imports
62
+
63
+ - Group imports with a blank line between groups.
64
+ - Favor module imports over wildcard imports.
65
+ - Keep Matplotlib usage explicit: `import matplotlib.pyplot as plt`.
66
+ - Keep NumPy usage explicit: `import numpy as np`.
67
+
68
+ ### Types
69
+
70
+ - Add type hints for public APIs and data structures.
71
+ - Use `typing` module constructs where helpful (e.g., `Sequence`, `Mapping`).
72
+ - Prefer `dict[str, Any]` or `Mapping[str, Any]` for free-form context.
73
+ - Use `dataclasses` for schema-like objects when appropriate.
74
+
75
+ ### Naming
76
+
77
+ - Functions and variables: `snake_case`.
78
+ - Classes and dataclasses: `PascalCase`.
79
+ - Constants: `UPPER_SNAKE_CASE`.
80
+ - Keep rule/diagnostic identifiers stable and deterministic.
81
+
82
+ ### Error Handling
83
+
84
+ - Fail fast on invariant violations when appropriate.
85
+ - Use exceptions for programmer errors; return structured warnings for data
86
+ issues that should not crash the exporter.
87
+ - Report non-fatal issues in the report `warnings[]` field.
88
+ - Prefer explicit error messages over silent fallbacks.
89
+
90
+ ### Determinism and Reproducibility
91
+
92
+ - Stable ordering: axes by `fig.axes`, series by `ax.lines`.
93
+ - Stable IDs: derive from axis index + series index + label.
94
+ - Numeric rounding must be controlled by a single `float_precision` parameter.
95
+ - Use a deterministic decimation strategy when `n > max_points`.
96
+
97
+ ### JSON + Markdown Outputs
98
+
99
+ - Always emit `<stem>.plot.json` and `<stem>.plot.md` sidecars.
100
+ - Keep the schema version pinned and documented.
101
+ - Prefer predictable key ordering when serializing.
102
+
103
+ ### Matplotlib / Headless
104
+
105
+ - Use the Agg backend for test figures and headless runs.
106
+ - PNGs are secondary; JSON is the source of truth for agents.
107
+
108
+ ### Diagnostics and Invariants
109
+
110
+ - Diagnostics are numeric and data-driven, not visual.
111
+ - Invariants encode expectations, not observations.
112
+ - Domain-specific rules must remain in consumer projects.
113
+ - Report FAIL invariants first; WARN invariants must include rationale.
114
+
115
+ ### Tests and Fixtures
116
+
117
+ - Tests should be deterministic and fast.
118
+ - Prefer small synthetic fixtures for core engine tests.
119
+ - Keep fixtures in `tests/fixtures/` (synthetic, expected, curated).
120
+ - Avoid brittle golden-value assertions until the schema stabilizes.
121
+
122
+ ## Agent Workflow Requirements (from `agent.md`)
123
+
124
+ Use this as the default workflow when adding or modifying behavior.
125
+
126
+ 1) Generate 2-3 minimal figures for smoke/regression:
127
+ - Happy path line plot (strictly increasing x, smooth y)
128
+ - Pathology with NaNs/inf and non-monotonic x
129
+ - Pathology with wiggles/jumps/outliers
130
+ 2) Export sidecars for each figure:
131
+ - PNG
132
+ - `.plot.json`
133
+ - `.plot.md`
134
+ 3) Inspect JSON before PNGs:
135
+ - FAIL invariants first
136
+ - WARN invariants next
137
+ - Diagnostics for rationale
138
+ 4) Propose fixes, re-run, compare reports.
139
+
140
+ CI / regression intent:
141
+
142
+ - Store `.plot.json` fixtures for the 2-3 minimal figures.
143
+ - CI should assert zero FAILs on happy path.
144
+ - CI should assert expected FAIL/WARN signatures on pathology cases.
145
+ - Use JSON diffs for regression review.
146
+
147
+ Escalation policy:
148
+
149
+ - FAIL invariants: stop and propose fix.
150
+ - WARN invariants: justify or refine rule.
151
+ - Clean report: accept plot without image review.
152
+
153
+ ## Preferred Structure (from docs)
154
+
155
+ The implementation plan proposes this layout (once implemented):
156
+
157
+ - `mpl_plot_report/api.py`: `dump_report()` and `build_report()`
158
+ - `mpl_plot_report/extract.py`: figure/axes/line extraction
159
+ - `mpl_plot_report/diagnostics.py`: numeric metrics
160
+ - `mpl_plot_report/rules.py`: `Rule`, `RuleResult`, `RuleSet`
161
+ - `mpl_plot_report/schema.py`: dataclasses + schema version
162
+ - `mpl_plot_report/render_md.py`: Markdown renderer
163
+ - `mpl_plot_report/util.py`: hashing/rounding/decimation
164
+
165
+ If you add these files, follow the conventions above.
166
+
167
+ ## README Expectations (future)
168
+
169
+ Once core API and tests are stable, update `README.md` with:
170
+
171
+ - Installation (uv/pip)
172
+ - Minimal usage snippet calling `dump_report`
173
+ - Expected output files (`.png`, `.plot.json`, `.plot.md`)
174
+ - How to supply a consumer `ruleset`
175
+ - How to run tests and where fixtures live
176
+
177
+ ## Notes for Agents
178
+
179
+ - Favor clear, minimal diffs; avoid large refactors unless required.
180
+ - Keep the engine domain-neutral; do not encode project-specific rules.
181
+ - Prefer small, composable helpers over monolithic functions.
182
+ - When in doubt, document the reasoning in the report output.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 placerte
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: mpl-plot-report
3
+ Version: 0.1.0
4
+ Summary: Structured plot metadata and diagnostics for Matplotlib (agent-friendly sidecars)
5
+ Author: Pierre Lacerte
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.9
9
+ Requires-Dist: matplotlib
10
+ Requires-Dist: numpy
11
+ Description-Content-Type: text/markdown
12
+
13
+ # mpl-plot-report
14
+
15
+ Export Matplotlib figures as deterministic JSON + Markdown sidecars for
16
+ LLM-friendly plot debugging. PNGs are emitted as secondary artifacts.
17
+
18
+ ## Installation
19
+
20
+ Using uv:
21
+
22
+ ```bash
23
+ uv sync
24
+ ```
25
+
26
+ Using pip:
27
+
28
+ ```bash
29
+ python -m venv .venv
30
+ . .venv/bin/activate
31
+ pip install -e .[dev]
32
+ ```
33
+
34
+ ## Minimal usage
35
+
36
+ ```python
37
+ import matplotlib
38
+
39
+ matplotlib.use("Agg")
40
+ import matplotlib.pyplot as plt
41
+ import numpy as np
42
+
43
+ from mpl_plot_report import dump_report
44
+
45
+ x = np.linspace(0, 10, 200)
46
+ y = np.sin(x)
47
+ fig, ax = plt.subplots()
48
+ ax.plot(x, y, label="signal")
49
+
50
+ dump_report(
51
+ fig,
52
+ out_dir="plots/run_001",
53
+ stem="signal",
54
+ context={"run_id": "run_001", "style_name": "default"},
55
+ )
56
+ ```
57
+
58
+ ## CLI usage
59
+
60
+ You can point the CLI at a module that exposes a callable returning a
61
+ Matplotlib Figure (default callable name: `make_figure`).
62
+
63
+ ```bash
64
+ uv run mpl-plot-report --module my_module --callable make_figure --out-dir plots/run_001 --stem signal
65
+ ```
66
+
67
+ Or provide a run id and let the CLI create `plots/<run_id>` automatically:
68
+
69
+ ```bash
70
+ uv run mpl-plot-report --module my_module --run-id run_001 --stem signal
71
+ ```
72
+
73
+ ## Output files
74
+
75
+ For a stem like `signal`, the exporter writes:
76
+
77
+ - `signal.png`
78
+ - `signal.plot.json`
79
+ - `signal.plot.md`
80
+
81
+ The JSON is the source of truth for agents and CI. Markdown is a concise
82
+ human-facing summary, and PNGs are optional.
83
+
84
+ ## Rulesets
85
+
86
+ Domain rules should live in consumer projects and be injected at runtime.
87
+ Define a `RuleSet` (or iterable of `Rule`) and pass it to `dump_report`:
88
+
89
+ ```python
90
+ from mpl_plot_report import dump_report
91
+ from mpl_plot_report.rules import Rule, RuleSet
92
+
93
+
94
+ def always_ok(report):
95
+ return True, "ok", None
96
+
97
+
98
+ ruleset = RuleSet([Rule("demo.always_ok", "warn", always_ok)])
99
+ dump_report(fig, out_dir="plots", stem="example", ruleset=ruleset)
100
+ ```
101
+
102
+ See a minimal module example at `examples/ruleset_demo.py`.
103
+
104
+ ## Tests
105
+
106
+ ```bash
107
+ uv run pytest
108
+ ```
109
+
110
+ Single test examples:
111
+
112
+ ```bash
113
+ uv run pytest tests/test_report.py
114
+ uv run pytest tests/test_report.py::test_dump_report_creates_sidecars
115
+ ```
116
+
117
+ ## Fixtures
118
+
119
+ Synthetic fixtures live under `tests/fixtures/expected/`. Regenerate them with:
120
+
121
+ ```bash
122
+ uv run python scripts/generate_fixtures.py
123
+ ```
@@ -0,0 +1,111 @@
1
+ # mpl-plot-report
2
+
3
+ Export Matplotlib figures as deterministic JSON + Markdown sidecars for
4
+ LLM-friendly plot debugging. PNGs are emitted as secondary artifacts.
5
+
6
+ ## Installation
7
+
8
+ Using uv:
9
+
10
+ ```bash
11
+ uv sync
12
+ ```
13
+
14
+ Using pip:
15
+
16
+ ```bash
17
+ python -m venv .venv
18
+ . .venv/bin/activate
19
+ pip install -e .[dev]
20
+ ```
21
+
22
+ ## Minimal usage
23
+
24
+ ```python
25
+ import matplotlib
26
+
27
+ matplotlib.use("Agg")
28
+ import matplotlib.pyplot as plt
29
+ import numpy as np
30
+
31
+ from mpl_plot_report import dump_report
32
+
33
+ x = np.linspace(0, 10, 200)
34
+ y = np.sin(x)
35
+ fig, ax = plt.subplots()
36
+ ax.plot(x, y, label="signal")
37
+
38
+ dump_report(
39
+ fig,
40
+ out_dir="plots/run_001",
41
+ stem="signal",
42
+ context={"run_id": "run_001", "style_name": "default"},
43
+ )
44
+ ```
45
+
46
+ ## CLI usage
47
+
48
+ You can point the CLI at a module that exposes a callable returning a
49
+ Matplotlib Figure (default callable name: `make_figure`).
50
+
51
+ ```bash
52
+ uv run mpl-plot-report --module my_module --callable make_figure --out-dir plots/run_001 --stem signal
53
+ ```
54
+
55
+ Or provide a run id and let the CLI create `plots/<run_id>` automatically:
56
+
57
+ ```bash
58
+ uv run mpl-plot-report --module my_module --run-id run_001 --stem signal
59
+ ```
60
+
61
+ ## Output files
62
+
63
+ For a stem like `signal`, the exporter writes:
64
+
65
+ - `signal.png`
66
+ - `signal.plot.json`
67
+ - `signal.plot.md`
68
+
69
+ The JSON is the source of truth for agents and CI. Markdown is a concise
70
+ human-facing summary, and PNGs are optional.
71
+
72
+ ## Rulesets
73
+
74
+ Domain rules should live in consumer projects and be injected at runtime.
75
+ Define a `RuleSet` (or iterable of `Rule`) and pass it to `dump_report`:
76
+
77
+ ```python
78
+ from mpl_plot_report import dump_report
79
+ from mpl_plot_report.rules import Rule, RuleSet
80
+
81
+
82
+ def always_ok(report):
83
+ return True, "ok", None
84
+
85
+
86
+ ruleset = RuleSet([Rule("demo.always_ok", "warn", always_ok)])
87
+ dump_report(fig, out_dir="plots", stem="example", ruleset=ruleset)
88
+ ```
89
+
90
+ See a minimal module example at `examples/ruleset_demo.py`.
91
+
92
+ ## Tests
93
+
94
+ ```bash
95
+ uv run pytest
96
+ ```
97
+
98
+ Single test examples:
99
+
100
+ ```bash
101
+ uv run pytest tests/test_report.py
102
+ uv run pytest tests/test_report.py::test_dump_report_creates_sidecars
103
+ ```
104
+
105
+ ## Fixtures
106
+
107
+ Synthetic fixtures live under `tests/fixtures/expected/`. Regenerate them with:
108
+
109
+ ```bash
110
+ uv run python scripts/generate_fixtures.py
111
+ ```
@@ -0,0 +1,95 @@
1
+ ## Agent Workflow — Plot Report Sidecars
2
+
3
+ ### Objective
4
+ Enable blind-friendly, deterministic critique of plots by agents using structured plot reports instead of rendered images.
5
+
6
+ ---
7
+
8
+ ### Minimal Test Figures (agent-generated)
9
+ The agent should generate 2–3 figures as a smoke/regression harness for the plot-report engine:
10
+
11
+ 1) **Happy path line plot**
12
+ - x strictly increasing
13
+ - smooth y
14
+ - should produce zero FAILs and minimal WARNs
15
+
16
+ 2) **Pathology: NaNs / inf / ordering**
17
+ - inject NaNs or inf in y
18
+ - optionally shuffle a segment of x (non-monotonic)
19
+ - should surface non-finite warnings and ordering/dx invariants
20
+
21
+ 3) **Pathology: wiggles / jumps / outliers**
22
+ - piecewise y with a step/jump + high-frequency wiggle region
23
+ - should trigger outlier metrics and wiggle/curvature diagnostics
24
+
25
+ These plots must be generated headlessly (Agg) and exported to:
26
+ - PNG
27
+ - `.plot.json`
28
+ - `.plot.md`
29
+
30
+ ---
31
+
32
+ ### Standard Debugging Loop
33
+
34
+ 1. **Run** plotting code to generate figures
35
+ 2. **Export** sidecar reports:
36
+ - `<stem>.plot.json`
37
+ - `<stem>.plot.md`
38
+ 3. **Agent inspects JSON**:
39
+ - invariant failures (FAIL first)
40
+ - warnings (NaN, decimation, ordering issues)
41
+ - suspicious diagnostics (wiggles, slope variance, outliers)
42
+ 4. **Agent proposes changes**:
43
+ - data corrections (ordering, NaN handling)
44
+ - plotting fixes (scale, limits, labels, units)
45
+ - invariant refinements (domain rules, if any)
46
+ 5. **Re-run and compare** reports
47
+
48
+ ---
49
+
50
+ ### What the Agent Should Rely On
51
+
52
+ - JSON report as the primary truth
53
+ - Invariant results as correctness signals
54
+ - Diagnostics to explain *why* something failed
55
+
56
+ PNG viewing is secondary and optional.
57
+
58
+ ---
59
+
60
+ ### What the Agent Must Avoid
61
+
62
+ - Judging correctness from PNGs alone
63
+ - Guessing intent without reading invariants
64
+ - Adding domain rules to the generic engine
65
+
66
+ ---
67
+
68
+ ### CI / Regression Strategy
69
+
70
+ - Store `.plot.json` fixtures for the 2–3 test figures above
71
+ - CI asserts:
72
+ - zero FAIL invariants for the happy-path figure
73
+ - expected FAIL/WARN signatures for pathology figures
74
+ - JSON diffs used for regression review
75
+
76
+ ---
77
+
78
+ ### Escalation Policy
79
+
80
+ - FAIL invariants → stop and propose fix
81
+ - WARN invariants → justify or refine rule
82
+ - Clean report → accept plot without image review
83
+
84
+ ---
85
+
86
+ ### After Green Tests
87
+
88
+ Once the package implementation is complete and tests pass:
89
+ - Draft a practical `README.md` for the standalone repo covering:
90
+ - installation (uv/pip)
91
+ - minimal Python usage (one small snippet calling `dump_report`)
92
+ - expected output files (`.png`, `.plot.json`, `.plot.md`)
93
+ - how to supply a `ruleset` from a consumer project
94
+ - how to run tests and where fixtures live
95
+
@@ -0,0 +1,161 @@
1
+ # Handoff — Make `.plot.md` verbose, explicit, and agent-friendly
2
+
3
+ ## Objective
4
+ Upgrade `mpl_plot_report` so the generated Markdown sidecar (`<stem>.plot.md`) is **useful on its own** for both humans and agents.
5
+
6
+ Current behavior is too minimal when there are no warnings/invariants; it often prints only counts and headings.
7
+
8
+ Target behavior:
9
+ - `.plot.json` remains the source of truth
10
+ - `.plot.md` becomes a structured, readable projection of the JSON
11
+ - Do **not** dump full point arrays; include summaries and optional small samples
12
+ - Include a link/embed reference to the PNG so a human can open it quickly
13
+
14
+ ---
15
+
16
+ ## Required `.plot.md` content (v1)
17
+
18
+ ### 0) Header
19
+ Include:
20
+ - Title: `<stem>`
21
+ - Timestamp / run_id (from `context`)
22
+ - git_hash (from `context`, if present)
23
+ - inputs + key parameters (from `context`)
24
+ - style identity: style name + rcParams hash (from `context.style`)
25
+
26
+ ### 1) PNG reference
27
+ Add a prominent section near the top:
28
+ - If PNG is in the same folder: include markdown image reference `![](<stem>.png)`
29
+ - This works on GitHub-rendered markdown and many viewers.
30
+ - Also include a plain path line for non-rendering environments.
31
+
32
+ ### 2) Warnings
33
+ - Always print a `Warnings` section.
34
+ - If empty, explicitly print `None`.
35
+
36
+ ### 3) Invariants / Rules
37
+ - Always print an `Invariant Results` section.
38
+ - Show FAIL first, then WARN, then PASS (if present).
39
+ - For each result:
40
+ - rule_id
41
+ - severity
42
+ - passed
43
+ - message
44
+ - where (axis index, series id/label, index ranges)
45
+
46
+ ### 4) Axes details (per axis)
47
+ For each axis, print:
48
+ - title
49
+ - xlabel / ylabel
50
+ - xscale / yscale
51
+ - xlim / ylim
52
+ - legend entries (or `None`)
53
+
54
+ ### 5) Series details (per series)
55
+ For each series (v1: `Line2D`):
56
+ - series id
57
+ - label
58
+ - kind
59
+
60
+ **Stats**
61
+ - n
62
+ - x_min/x_max, y_min/y_max
63
+ - first point / last point
64
+
65
+ **Diagnostics** (print all fields in a stable order)
66
+ - Non-finite: count + indices/ranges if available
67
+ - dx sanity: min/median/non-positive count
68
+ - dy outliers: max |dy|, robust z max, outlier count
69
+ - slope stats: mean/median/std + quantiles if present
70
+ - wiggles: sign-change count, wiggle score
71
+ - curvature: std, max abs, sign changes
72
+
73
+ **Data sample (optional but recommended)**
74
+ - If `include_data=True`, do not dump full arrays.
75
+ - Print:
76
+ - decimation info (original_n, max_points, method)
77
+ - first 5 (x,y) pairs
78
+ - last 5 (x,y) pairs
79
+
80
+ ### 6) JSON echo (optional)
81
+ If you want “expose all JSON data” without full points:
82
+ - Add a final section `Raw JSON (minus points)`
83
+ - Print a pretty JSON block where `series[].data.x` and `series[].data.y` are replaced with:
84
+ - `{"omitted": true, "n": <n>, "sample": {"head": [...], "tail": [...]}}`
85
+
86
+ This makes MD self-contained while avoiding megabytes of data.
87
+
88
+ ---
89
+
90
+ ## API changes (allowed)
91
+ Keep existing API stable. If you add knobs, make them optional:
92
+
93
+ - `md_mode: Literal["summary","full"] = "full"` (default to full to satisfy this spec)
94
+ - `md_data_sample: int = 5` (head/tail sample size)
95
+ - `md_include_png_embed: bool = True`
96
+
97
+ No breaking changes.
98
+
99
+ ---
100
+
101
+ ## Implementation notes
102
+
103
+ ### Where to implement
104
+ - `mpl_plot_report/render_md.py`
105
+ - Add a `render_report_full(report: dict, *, stem: str | None = None, md_data_sample: int = 5, md_include_png_embed: bool = True) -> str`
106
+ - Keep the existing renderer as `summary` or remove if unused.
107
+
108
+ ### Stable ordering
109
+ Make the Markdown diff-friendly:
110
+ - Use deterministic ordering for:
111
+ - axes
112
+ - series
113
+ - diagnostic keys (define an explicit list)
114
+
115
+ ### Avoid point dumps
116
+ Even if JSON contains decimated points, MD should only show head/tail samples.
117
+
118
+ ---
119
+
120
+ ## Tests to add
121
+
122
+ 1) `test_md_full_has_required_sections`
123
+ - Generate the happy-path synthetic figure
124
+ - Assert `.plot.md` includes:
125
+ - PNG embed line
126
+ - Context header fields
127
+ - Warnings section
128
+ - Invariant Results section
129
+ - Axes and Series sections
130
+
131
+ 2) `test_md_reports_nonfinite_and_ordering`
132
+ - Generate pathology figure (NaN + non-monotonic x)
133
+ - Assert `.plot.md` contains:
134
+ - non-finite count > 0
135
+ - dx_non_positive_count > 0 (or equivalent)
136
+ - warning(s)
137
+
138
+ 3) `test_md_does_not_dump_full_arrays`
139
+ - Generate a long series (n >> max_points)
140
+ - Assert `.plot.md` length stays under a reasonable bound or that it contains `head`/`tail` samples and does not contain thousands of comma-separated floats.
141
+
142
+ ---
143
+
144
+ ## Definition of done
145
+ - `.plot.md` is substantially informative even when warnings/invariants are empty.
146
+ - PNG is referenced at top.
147
+ - All diagnostics visible (not hidden in JSON only).
148
+ - Point arrays not dumped; only samples.
149
+ - Tests pass headlessly.
150
+
151
+ ---
152
+
153
+ ## After this task
154
+ Once the MD verbosity upgrade is complete and tests pass:
155
+ - Draft `README.md` for the repo:
156
+ - installation (uv/pip)
157
+ - minimal usage example calling `dump_report`
158
+ - explain the 3 artifacts (`.png`, `.plot.json`, `.plot.md`)
159
+ - how to inject a `ruleset` from consumer projects
160
+ - how to run tests + fixtures layout
161
+