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.
- mpl_plot_report-0.1.0/.gitignore +1 -0
- mpl_plot_report-0.1.0/.python-version +1 -0
- mpl_plot_report-0.1.0/AGENTS.md +182 -0
- mpl_plot_report-0.1.0/LICENSE +21 -0
- mpl_plot_report-0.1.0/PKG-INFO +123 -0
- mpl_plot_report-0.1.0/README.md +111 -0
- mpl_plot_report-0.1.0/agent.md +95 -0
- mpl_plot_report-0.1.0/docs/handoff.md +161 -0
- mpl_plot_report-0.1.0/docs/implementation.md +213 -0
- mpl_plot_report-0.1.0/docs/specs.md +95 -0
- mpl_plot_report-0.1.0/examples/ruleset_demo.py +10 -0
- mpl_plot_report-0.1.0/main.py +6 -0
- mpl_plot_report-0.1.0/mpl-plot-report-dump-20260202-0819.txt +3784 -0
- mpl_plot_report-0.1.0/mpl_plot_report/__init__.py +5 -0
- mpl_plot_report-0.1.0/mpl_plot_report/api.py +111 -0
- mpl_plot_report-0.1.0/mpl_plot_report/cli.py +99 -0
- mpl_plot_report-0.1.0/mpl_plot_report/diagnostics.py +80 -0
- mpl_plot_report-0.1.0/mpl_plot_report/extract.py +134 -0
- mpl_plot_report-0.1.0/mpl_plot_report/render_md.py +244 -0
- mpl_plot_report-0.1.0/mpl_plot_report/rules.py +46 -0
- mpl_plot_report-0.1.0/mpl_plot_report/schema.py +3 -0
- mpl_plot_report-0.1.0/mpl_plot_report/util.py +63 -0
- mpl_plot_report-0.1.0/pyproject.toml +14 -0
- mpl_plot_report-0.1.0/scripts/generate_fixtures.py +72 -0
- mpl_plot_report-0.1.0/tests/conftest.py +8 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/happy_path.plot.json +493 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/happy_path.plot.md +205 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/happy_path.png +0 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/nan_ordering.plot.json +116 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/nan_ordering.plot.md +214 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/nan_ordering.png +0 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/wiggles_outliers.plot.json +893 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/wiggles_outliers.plot.md +205 -0
- mpl_plot_report-0.1.0/tests/fixtures/expected/wiggles_outliers.png +0 -0
- mpl_plot_report-0.1.0/tests/fixtures/synthetic/crowded_and_two_peaks_hit_010.csv +68356 -0
- mpl_plot_report-0.1.0/tests/fixtures/synthetic/crowded_but_clean_hit_013.csv +68356 -0
- mpl_plot_report-0.1.0/tests/fixtures/synthetic/crowded_but_coupled_hit_009.csv +68356 -0
- mpl_plot_report-0.1.0/tests/test_report.py +114 -0
- 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 ``
|
|
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
|
+
|