Skip to content

Commit 2b1ce7b

Browse files
vosesoftclaude
andcommitted
Release v0.3.0-alpha.20: fix histogram chart, polish layout
Polish pass on build_executive_report after live screenshot review against NPV_of_a_capital_investment workbook. Histogram chart was inverted (#18b) Prior SetSourceData on the full 3-column [Bin, Count, Cumulative %] block made Excel turn ALL THREE columns into data series — visible result was bin centres plotted as bars (50k, 100k, 150k...), Count plotted as a red line, Cumulative invisible. Fix: bind only to Count + Cumulative columns, then explicitly assign SeriesCollection(i).XValues to the Bin column on both series. Now: proper frequency-histogram bars on bin centres, cumulative-% line overlay on secondary Y axis. Layout polish (column A now a narrow gutter) - Content shifted right by one column: title band B:L (was A:J), headlines B-F (was A-E), output name at I (was G), stats table B-H (was A-G), callouts B-merged-to-L (was A-merged-to-J). - Narrow gutters at A and M (width 2 each) for visual breathing room. - Stats-table CV column widened to 16 — no more ##### overflow on values like 1.296. - Alternating row tint on stats table for readability. - High-CV cells bold in addition to coloured (survives print-to-PDF). - Chart sizes bumped 400x240 / 360x240 (was 380x220 / 340x220). 399 tests pass. 4 existing tests updated for B-shifted columns. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 12a8b72 commit 2b1ce7b

8 files changed

Lines changed: 183 additions & 105 deletions

File tree

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,30 @@ All notable changes to ModelRisk MCP. Follows [Keep a Changelog](https://keepach
44

55
## [Unreleased]
66

7+
## [0.3.0-alpha.20] — 2026-05-22
8+
9+
Polish pass on `build_executive_report` after a live screenshot review against the `NPV_of_a_capital_investment` workbook. Two real issues found: the histogram chart was rendering with completely wrong semantics, and the layout was visually cramped with column A pulling double duty as label-holder and edge.
10+
11+
### Fixed
12+
13+
- **Histogram chart was inverted (#18b).** Prior versions called `SetSourceData` on the full 3-column block `[Bin, Count, Cumulative %]`, which made Excel turn all three columns into data series — visible result was "Bin" values plotted as random-height blue bars (the user's screenshot showed bars at 50k, 100k, 150k etc.), "Count" plotted as a red line, and Cumulative invisible. Fix: bind to ONLY the Count + Cumulative columns, then explicitly assign `SeriesCollection(i).XValues` on both series to the Bin column. Result: proper frequency-histogram bars with bin centres on the X axis, cumulative-% line overlay on a secondary Y axis. The chart now looks like what the report description says.
14+
15+
### Polished
16+
17+
- **Column A is now a narrow gutter (width 2).** Previously content started flush against the left edge with column A holding both labels AND being the page edge. Now the layout has narrow gutters at A and M, with content in B–L. Same change applied to the title band merge range (B:L instead of A:J), headline numbers (MEAN now at B6 instead of A6), stats table (Output at B26 instead of A26), and callouts (`• ...` at B instead of A).
18+
- **Stats-table CV column no longer overflows.** Bumped width to 16 (was 14 implicit) so values like `1.296` render in full instead of `####`.
19+
- **Alternating row tint** on the stats table for readability when there are multiple outputs.
20+
- **High-CV cells now bold** in addition to coloured, so they survive print-to-PDF where colour fidelity drops.
21+
- **Chart sizes bumped** to 400×240 (histogram) and 360×240 (tornado) — the original 380×220 / 340×220 felt small relative to the title band. Charts shifted right by ~16pt to align with the new column-B content start.
22+
23+
### Why this matters
24+
25+
The report is the primary user-facing deliverable. A broken chart isn't "a bug to fix later" — it's the LLM lying to the user about what got built (`chart_count: 2` while the chart was visually wrong). Same goes for the cramped layout: a stakeholder sees the report and forms an opinion about modelrisk-mcp from that single screenshot. Worth getting right.
26+
27+
### Tests
28+
29+
399 unit tests pass. 4 existing tests updated to assert against the new column positions (B-shifted).
30+
731
## [0.3.0-alpha.19] — 2026-05-22
832

933
Fixes the bug-#23 lookup-after-samples regression discovered while end-to-end testing alpha.18 against a real workbook: `get_sensitivity_ranking` returned empty on the first call after `run_simulation`, then worked on the second identical call. The diagnostic trace was unambiguous — the output looked up fine, its samples loaded, then every input lookup against the same handle returned None.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "modelrisk-mcp"
3-
version = "0.3.0a19"
3+
version = "0.3.0a20"
44
description = "Open MCP server bridging Anthropic Claude (and any MCP-compatible client) with the ModelRisk Excel add-in."
55
readme = "README.md"
66
requires-python = ">=3.11"

server.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
"name": "io.github.vosesoftware/modelrisk-mcp",
44
"title": "ModelRisk",
55
"description": "Read, build, fit, and run Monte Carlo risk models in Excel through Vose Software's ModelRisk.",
6-
"version": "0.3.0a19",
6+
"version": "0.3.0a20",
77
"packages": [
88
{
99
"registryType": "pypi",
1010
"identifier": "modelrisk-mcp",
11-
"version": "0.3.0a19",
11+
"version": "0.3.0a20",
1212
"transport": {
1313
"type": "stdio"
1414
}

src/modelrisk_mcp/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.3.0a19"
1+
__version__ = "0.3.0a20"

src/modelrisk_mcp/bridge/reports.py

Lines changed: 136 additions & 85 deletions
Large diffs are not rendered by default.

tests/unit/test_reports.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,9 @@ def test_creates_sheet_with_title_and_subtitle(self) -> None:
267267
)
268268
assert isinstance(result, ExecutiveReportResult)
269269
sheet = book.sheets["Report"]
270-
assert sheet.cells.get("A1") == "NPV under uncertainty"
271-
assert sheet.cells.get("A2") == "5,000 iterations · seed 1 · 2026-05-22"
270+
# alpha.20 layout shift: content starts at column B (A is gutter).
271+
assert sheet.cells.get("B1") == "NPV under uncertainty"
272+
assert sheet.cells.get("B2") == "5,000 iterations · seed 1 · 2026-05-22"
272273
assert sheet.activated is True
273274

274275
def test_headline_numbers_in_expected_cells(self) -> None:
@@ -284,12 +285,13 @@ def test_headline_numbers_in_expected_cells(self) -> None:
284285
sensitivity=_make_sensitivity(),
285286
)
286287
sheet = book.sheets["Report"]
287-
assert sheet.cells.get("A6") == 1000.0 # Mean
288-
assert sheet.cells.get("C6") == 1000.0 # P50 = mean for symmetric
289-
assert sheet.cells.get("E6") == 200.0 # Stdev
288+
# alpha.20 layout: B=Mean, C=P5, D=P50, E=P-hi, F=Stdev
289+
assert sheet.cells.get("B6") == 1000.0 # Mean
290+
assert sheet.cells.get("D6") == 1000.0 # P50 = mean for symmetric
291+
assert sheet.cells.get("F6") == 200.0 # Stdev
290292
# Headline label row
291-
assert sheet.cells.get("A5") == "MEAN"
292-
assert sheet.cells.get("E5") == "STDEV"
293+
assert sheet.cells.get("B5") == "MEAN"
294+
assert sheet.cells.get("F5") == "STDEV"
293295

294296
def test_stats_table_includes_secondary_outputs(self) -> None:
295297
book = _FakeBook()
@@ -309,11 +311,11 @@ def test_stats_table_includes_secondary_outputs(self) -> None:
309311
secondary_results=secondary,
310312
)
311313
sheet = book.sheets["Report"]
312-
# Stats table starts at row 26 (header), data at 27+
313-
assert sheet.cells.get("A26") == "Output"
314-
assert sheet.cells.get("A27") == "Profit" # primary first
315-
assert sheet.cells.get("A28") == "Cost"
316-
assert sheet.cells.get("A29") == "Revenue"
314+
# alpha.20 layout: stats table shifted to columns B..H.
315+
assert sheet.cells.get("B26") == "Output"
316+
assert sheet.cells.get("B27") == "Profit" # primary first
317+
assert sheet.cells.get("B28") == "Cost"
318+
assert sheet.cells.get("B29") == "Revenue"
317319

318320
def test_callouts_generated_from_data(self) -> None:
319321
book = _FakeBook()
@@ -334,7 +336,7 @@ def test_callouts_generated_from_data(self) -> None:
334336
# CALLOUTS header on first callout row.
335337
callout_cells = [
336338
v for k, v in sheet.cells.items()
337-
if k.startswith("A") and isinstance(v, str)
339+
if k.startswith("B") and isinstance(v, str)
338340
and v.startswith("•")
339341
]
340342
assert len(callout_cells) >= 2
@@ -363,7 +365,8 @@ def test_replaces_existing_sheet(self) -> None:
363365
# New sheet has the fresh content.
364366
new_sheet = book.sheets["Report"]
365367
assert new_sheet is not existing
366-
assert new_sheet.cells.get("A1") == "fresh"
368+
# alpha.20: title shifted from A1 to B1.
369+
assert new_sheet.cells.get("B1") == "fresh"
367370

368371
def test_empty_samples_doesnt_crash(self) -> None:
369372
"""If get_samples returned [] (no .vmrs / empty result), the

tests/unit/test_server_boot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
def test_version_is_set() -> None:
8-
assert __version__ == "0.3.0a19"
8+
assert __version__ == "0.3.0a20"
99

1010

1111
def test_server_name() -> None:

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)