Skip to content

Commit 34981eb

Browse files
chore(release): bump to v1.15.2 — MCP strict-JSON + dual-track replicate + packaging hardening
Patch release on top of v1.15.1 with no estimator numerical change. Three independent hardening tracks land together: - sp.agent.mcp_server is now strict-JSON-clean over the wire — native NaN / ±Infinity floats are walked to null before serialisation so RFC 8259 parsers (Claude Desktop included) never see a token they reject. Covered by tests/test_mcp_nan_inf.py. - sp.replicate graduates four canonical replications — Card (1995), Abadie-Diamond-Hainmueller (2010), Lalonde (1986) / DW (1999), Lee (2008) — from single-track stubs to full classic + modern recipes on bundled real CSVs with pinned golden numbers. - Release packaging tightens — wheel smoke tests fail loudly on ImportError, py.typed ships in the wheel, the result _repr_html_ path escapes user-controlled cells (notebook XSS-safety), explicit formulaic dependency, modern PEP 639 license metadata, and a new [text] extra wires sentence-transformers for sp.causal_text. Bumps pyproject.toml + __init__.py + CITATION.cff + docs/index.md + README BibTeX banner. README / README_CN / MIGRATION grow a 1.15.2 banner / migration section. CHANGELOG.md gains the [1.15.2] entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7aba2b6 commit 34981eb

9 files changed

Lines changed: 215 additions & 7 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ audit_report.json
191191
# Paper JSS and JOSS submissions
192192
Paper-JSS/
193193
Paper-JOSS/
194+
Paper-AgentBench/
194195

195196
# Temp files
196197
MORNING_REPORT_2026-04-29.md

CHANGELOG.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,140 @@ All notable changes to StatsPAI will be documented in this file.
44

55
## [Unreleased]
66

7+
## [1.15.2] — 2026-05-17
8+
9+
### Headline
10+
11+
Patch release on top of v1.15.1. **No estimator numerical paths change.**
12+
Three independent hardening tracks land together:
13+
14+
1. **Agent-native infrastructure**`sp.agent.mcp_server` is now strict-
15+
JSON-clean over the wire (no `NaN` / `Infinity` literals reaching
16+
Claude Desktop / RFC 8259 parsers), agent schema metadata extraction
17+
is more complete, and a new `text` extra makes
18+
`sentence-transformers` an explicit opt-in for the v1.6 `causal_text`
19+
surface instead of a soft import surprise.
20+
2. **`sp.replicate` dual-track** — Card (1995), Abadie-Diamond-
21+
Hainmueller (2010), Lalonde (1986) / DW (1999), and Lee (2008)
22+
replications are promoted from single-track stubs to full
23+
**classic + modern** recipes that ship with the original public-
24+
domain CSVs and pinned golden numbers.
25+
3. **Release packaging** — wheel smoke tests now **fail loudly** on
26+
import error, `py.typed` ships in the wheel for downstream
27+
`mypy --strict` consumers, the result `_repr_html_` path escapes
28+
user-controlled cell content (notebook XSS-safety), and the
29+
`formulaic` dependency that `sp.spatial` parses formulas with is
30+
declared explicitly instead of relying on `linearmodels`'s
31+
transitive resolution.
32+
33+
### Added — `text` optional extra
34+
35+
- New `[project.optional-dependencies] text = ["sentence-transformers>=2.2.0"]`
36+
in [`pyproject.toml`](pyproject.toml). The v1.6 `sp.causal_text` MVP
37+
used to lazy-import `sentence_transformers` and raise an opaque
38+
`ImportError` on first call. `pip install statspai[text]` now wires
39+
the dependency explicitly; the lazy import still triggers a clear
40+
pointer to the extra when missing.
41+
42+
### Added — `sp.replicate` dual-track guides for four canonical papers
43+
44+
- **Card (1995)** — proximity-to-college IV for returns to schooling.
45+
- **Abadie, Diamond & Hainmueller (2010)** — California Proposition 99
46+
synthetic-control.
47+
- **Lalonde (1986) / Dehejia-Wahba (1999)** — NSW + PSID-1 (MatchIt
48+
subset, `n=614`) propensity-score matching.
49+
- **Lee (2008)** — US Senate RD (`n=1390`) bandwidth-selected jump.
50+
51+
Each entry now ships a **classic track** (the estimator the paper
52+
used: 2SLS for Card, weighted synthetic-control for Abadie, naive
53+
OLS / adjusted OLS / 1:1 NN PSM for Lalonde, local-linear CCT RD
54+
for Lee) **and a modern track** (DML PLR + entropy balancing,
55+
bias-corrected robust RD, multi-method synth-compare). Real CSVs
56+
land under [`src/statspai/datasets/data/`](src/statspai/datasets/data/)
57+
(`card_1995.csv`, `california_prop99.csv`, `lalonde_matchit.csv`,
58+
`lee_2008_senate.csv`) and `sp.datasets.nsw_lalonde` /
59+
`sp.datasets.lee_2008_senate` gain a `simulated=False` real-data
60+
branch with published-paper benchmarks exposed via `df.attrs`. The
61+
Lalonde classic track now reproduces DW (1999) Table 3-4 within a
62+
$5 drift tolerance; the Lee CCT track returns Conv `7.414` and
63+
bias-corrected robust `7.507` (SE `1.741`, `h=17.754`), matching the
64+
R `rdrobust` reference. All 13 BibTeX keys cited across the four
65+
entries are verified in [`paper.bib`](paper.bib).
66+
67+
### Fixed — MCP wire format is strict-JSON-clean
68+
69+
- [`sp.agent.mcp_server`](src/statspai/agent/mcp_server.py) used to
70+
serialise responses with `json.dumps(..., default=_json_default)`,
71+
which **does not** intercept native Python `float('nan')` /
72+
`float('inf')` — those become the non-standard literals `NaN` /
73+
`Infinity` in the JSON output, which RFC 8259 parsers (including
74+
Claude Desktop's `JSON.parse`) reject with errors like
75+
`"No number after minus sign"`. Responses now pass through
76+
`_clean_floats` (recursively replaces `NaN` / `±Infinity` with
77+
`null` across `dict` / `list` / `tuple` containers) and serialise
78+
with `allow_nan=False`, so the server can never emit a JSON token a
79+
strict parser refuses. Covered by 273-line regression suite
80+
[`tests/test_mcp_nan_inf.py`](tests/test_mcp_nan_inf.py).
81+
- Agent schema metadata extraction (`sp.function_schema`,
82+
`sp.describe_function`) now surfaces more signature detail for
83+
registry entries built from auto-introspection.
84+
- Stability-tier audit (`scripts/stability_audit.py`) accounts for
85+
evidence files more precisely; new
86+
[`tests/test_agent_schema.py`](tests/test_agent_schema.py) locks
87+
the schema metadata fields agents rely on.
88+
89+
### Fixed — result HTML escaping (notebook XSS-safety)
90+
91+
- [`CausalResult._repr_html_`](src/statspai/core/results.py) (and the
92+
surrounding rich-display helpers) now route every user-derived cell
93+
through `html.escape`. Previously, any string column whose contents
94+
contained `<` / `>` / `&` / `"` would interpolate raw into the
95+
rendered HTML, opening a path for notebook XSS when a result was
96+
displayed in Jupyter / VS Code / nbviewer. New regression test:
97+
[`tests/test_results_html_escape.py`](tests/test_results_html_escape.py).
98+
99+
### Fixed — release packaging hygiene
100+
101+
- `pyproject.toml` bumps the build requirement to
102+
`setuptools>=77.0.0` and migrates `license = {text = "MIT"}` to the
103+
modern PEP 639 `license = "MIT"` + `license-files = ["LICENSE"]`
104+
pair. Drops the deprecated `License :: OSI Approved :: ...`
105+
classifier path implicitly.
106+
- [`MANIFEST.in`](MANIFEST.in) now includes `src/statspai/py.typed`
107+
and the sdist test fixtures so `pip install --no-binary :all:` and
108+
`mypy --strict` both behave correctly on the published artifacts.
109+
- [`.github/workflows/build-wheels.yml`](.github/workflows/build-wheels.yml)
110+
and [`.github/workflows/ci-cd.yml`](.github/workflows/ci-cd.yml):
111+
wheel smoke tests now **fail the job** on `ImportError` instead of
112+
swallowing it as a warning. Releases that silently ship a broken
113+
wheel are no longer possible from a green CI run.
114+
- New explicit dependency `formulaic>=0.6.0` in `dependencies`
115+
(`sp.spatial.*` parses Wilkinson formulas through it; relying on
116+
`linearmodels`'s transitive resolution broke when downstream users
117+
pinned older `linearmodels`).
118+
119+
### Docs
120+
121+
- JOSS submission [`paper.md`](paper.md) is rewritten for the Scott
122+
Rozelle review pass — tighter scope statement, cleaner schema
123+
description, explicit AI-use disclosure, 12 May 2026 submission
124+
date. Cited bibliography entries in [`paper.bib`](paper.bib) are
125+
refreshed to match.
126+
- README / README\_CN add the hero banner image
127+
([`docs/logo/readme-1.png`](docs/logo/readme-1.png)).
128+
- Track-C performance comparison table
129+
([`tests/perf/results/perf_table.tex`](tests/perf/results/perf_table.tex))
130+
switches to `\scriptsize` with package-name macros and a
131+
direction-aware "Winner" column; log-log figure regenerated to match.
132+
133+
### Internal
134+
135+
- Perf-benchmark harness factored — new
136+
[`tests/perf/_common.py`](tests/perf/_common.py) shared utilities;
137+
`tests/perf/05_feols_jax_bootstrap_bench.py` rewritten on top.
138+
- Full-suite validation snapshot refreshed in
139+
[`test_results_full_suite.md`](test_results_full_suite.md).
140+
7141
## [1.15.1] — 2026-05-07
8142

9143
### Headline

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ authors:
1616
given-names: "Biaoyue"
1717
affiliation: "Stanford REAP, Stanford University"
1818
email: "brycew6m@stanford.edu"
19-
version: "1.14.0"
20-
date-released: "2026-05-04"
19+
version: "1.15.2"
20+
date-released: "2026-05-17"
2121
license: MIT
2222
# Zenodo *concept* DOI — always resolves to the latest archived version.
2323
# For a specific-version DOI (e.g. for replication packages), follow the

MIGRATION.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,43 @@ Internal version-to-version migrations are at the top; the long-form
77

88
<a id="sp-rdrobust-bwselect-cct-r-parity-opt-in"></a>
99

10+
## v1.15.1 → v1.15.2 — strict-JSON MCP wire, dual-track replicate, packaging
11+
12+
**No estimator numerical path changes.** Three classes of consumers
13+
should take note:
14+
15+
- **`sp.agent.mcp_server` clients** (Claude Desktop / Codex / any
16+
RFC 8259-strict JSON parser). v1.15.1 could leak the non-standard
17+
literals `NaN` / `Infinity` / `-Infinity` into responses whenever an
18+
estimator surfaced a degenerate float (`np.nan` standard errors on a
19+
singular covariate, `inf` log-likelihood on a saturated model, etc).
20+
v1.15.2 walks all containers before `json.dumps` and serialises with
21+
`allow_nan=False`, replacing those values with `null`. **Action**:
22+
none — strict parsers that previously failed now succeed; lenient
23+
parsers see `null` where they used to see `NaN`. Update your
24+
downstream JSON Schema if it explicitly typed those fields as
25+
`number` (they should be `["number", "null"]`).
26+
27+
- **`sp.causal_text` users.** The MVP relied on a soft import of
28+
`sentence-transformers`. v1.15.2 adds an explicit
29+
`pip install statspai[text]` extra. The lazy import path is
30+
preserved, but the `ImportError` message now points at the extra
31+
instead of suggesting a bare `pip install sentence-transformers`.
32+
33+
- **`sp.replicate` users.** Entries for Card (1995), Abadie-Diamond-
34+
Hainmueller (2010), Lalonde (1986) / DW (1999), and Lee (2008) now
35+
return classic + modern recipes computed on the bundled real CSVs
36+
instead of single-track simulated stubs. If you were pinning to the
37+
v1.15.1 simulated numbers in CI, switch to the published-paper
38+
benchmarks now exposed via `df.attrs['paper_original']` (see
39+
`sp.datasets.nsw_lalonde(simulated=False)` and
40+
`sp.datasets.lee_2008_senate(simulated=False)`).
41+
42+
Existing `sp.rdrobust` / `sp.nbreg` / `sp.xtnbreg` / `sp.menbreg`
43+
call sites carry over unchanged from v1.15.1.
44+
45+
---
46+
1047
## v1.15.0 → v1.15.1 — `sp.rdrobust(bwselect='cct')` R-parity opt-in
1148

1249
**No breaking change.** `sp.rdrobust` keeps `bwselect='mserd'` (StatsPAI's

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,25 @@ StatsPAI's focus is **causal inference** — and on this axis we aim to be the m
128128

129129
---
130130

131+
**📦 v1.15.2 (2026-05-17) — Strict-JSON MCP wire + dual-track replicate guides + release-packaging hardening**
132+
133+
Patch release on top of v1.15.1 with **no estimator numerical change**.
134+
Three independent hardening tracks land together: (1) `sp.agent.mcp_server`
135+
now produces strict-JSON-clean output — native `NaN` / `±Infinity` floats
136+
are walked to `null` before serialisation so RFC 8259 parsers
137+
(including Claude Desktop) never see a token they reject; (2)
138+
`sp.replicate` graduates four canonical replications — Card (1995),
139+
Abadie-Diamond-Hainmueller (2010) California Prop 99, Lalonde (1986) /
140+
DW (1999), and Lee (2008) Senate RD — from single-track stubs to full
141+
**classic + modern** recipes on bundled real CSVs with pinned golden
142+
numbers; (3) release packaging tightens — wheel smoke tests fail loudly
143+
on `ImportError`, `py.typed` ships in the wheel, the result
144+
`_repr_html_` path escapes user-controlled cells (notebook XSS-safety),
145+
and a new `[text]` extra makes `sentence-transformers` an explicit
146+
opt-in for `sp.causal_text`. Install with
147+
`pip install --upgrade statspai`. Full notes in
148+
[`CHANGELOG.md`](CHANGELOG.md) under `[1.15.2]`.
149+
131150
**📦 v1.15.1 (2026-05-07) — R-parity RD opt-in + negative-binomial implementation notes**
132151

133152
Patch release preparing the PyPI cut after v1.15.0. `sp.rdrobust`
@@ -1344,7 +1363,7 @@ resolves to the latest version):
13441363
author = {Wang, Biaoyue},
13451364
title = {StatsPAI: The Agent-Native Causal Inference \& Econometrics Toolkit for Python},
13461365
year = {2026},
1347-
version = {1.15.1},
1366+
version = {1.15.2},
13481367
doi = {10.5281/zenodo.19933900},
13491368
url = {https://doi.org/10.5281/zenodo.19933900},
13501369
license = {MIT},

README_CN.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,23 @@ StatsPAI 聚焦**因果推断**——在这条主线上,我们的目标是成
5050

5151
---
5252

53+
**📦 v1.15.2(2026-05-17)— MCP 严格 JSON + Replicate 双轨指南 + 发布打包加固**
54+
55+
v1.15.1 之上的 patch 发布,**估计器数值路径零变化**,三条独立的加固
56+
线一起落地:(1)`sp.agent.mcp_server` 现在产出严格合规的 JSON——
57+
原生 `NaN` / `±Infinity` 浮点会在序列化前递归替换为 `null`,让 RFC
58+
8259 严格解析器(Claude Desktop 在内)不再因为非标准 token 报错;
59+
(2)`sp.replicate` 把四篇经典论文复现——Card (1995)、Abadie-
60+
Diamond-Hainmueller (2010) 加州 Prop 99、Lalonde (1986) / DW
61+
(1999)、Lee (2008) 参议员 RD——从单轨 stub 升级为**经典轨 + 现代轨**
62+
完整菜谱,配套打包了原始公共域 CSV 和锁定的金标准数字;(3)发布
63+
打包收紧——wheel 冒烟测试在 `ImportError`**失败而不是吞错**
64+
`py.typed` 标记打入 wheel,结果 `_repr_html_` 路径转义用户控制单元
65+
(notebook XSS 防御),新增 `[text]` extra 让 `sentence-transformers`
66+
成为 `sp.causal_text` 的显式可选依赖。升级命令:
67+
`pip install --upgrade statspai`。完整发布说明见
68+
[`CHANGELOG.md`](CHANGELOG.md) `[1.15.2]`
69+
5370
**📦 v1.15.1(2026-05-07)— RD 的 R 精确复现路径 + 负二项回归实现说明**
5471

5572
这是 v1.15.0 之后的 patch 发布准备版。`sp.rdrobust` 新增
@@ -564,7 +581,7 @@ sp.__citation__ # 与 sp.citation("bibtex") 等价
564581
author = {Wang, Biaoyue},
565582
title = {StatsPAI: The Agent-Native Causal Inference \& Econometrics Toolkit for Python},
566583
year = {2026},
567-
version = {1.15.1},
584+
version = {1.15.2},
568585
doi = {10.5281/zenodo.19933900},
569586
url = {https://doi.org/10.5281/zenodo.19933900},
570587
license = {MIT},

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ method that returns the correct BibTeX entry) and this package:
171171
title = {StatsPAI: A Unified, Agent-Native Python Toolkit for
172172
Causal Inference and Applied Econometrics},
173173
year = {2026},
174-
version = {1.15.1},
174+
version = {1.15.2},
175175
url = {https://github.com/brycewang-stanford/StatsPAI}
176176
}
177177
```

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "StatsPAI"
7-
version = "1.15.1"
7+
version = "1.15.2"
88
description = "The Agent-Native Causal Inference & Econometrics Toolkit for Python"
99
readme = "README.md"
1010
license = "MIT"

src/statspai/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
>>> sp.outreg2(result, filename="results.xlsx")
2323
"""
2424

25-
__version__ = "1.15.1"
25+
__version__ = "1.15.2"
2626
__author__ = "Biaoyue Wang"
2727
__email__ = "brycew6m@stanford.edu"
2828

0 commit comments

Comments
 (0)