stata-code 0.9.0__tar.gz → 0.9.1__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 (83) hide show
  1. {stata_code-0.9.0 → stata_code-0.9.1}/.gitignore +2 -0
  2. {stata_code-0.9.0 → stata_code-0.9.1}/CHANGELOG.md +151 -7
  3. {stata_code-0.9.0 → stata_code-0.9.1}/PKG-INFO +9 -2
  4. {stata_code-0.9.0 → stata_code-0.9.1}/PUBLISHING.md +16 -7
  5. {stata_code-0.9.0 → stata_code-0.9.1}/README.md +8 -1
  6. {stata_code-0.9.0 → stata_code-0.9.1}/SCHEMA.md +3 -1
  7. {stata_code-0.9.0 → stata_code-0.9.1}/docs/competitive-landscape.md +2 -2
  8. {stata_code-0.9.0 → stata_code-0.9.1}/pyproject.toml +1 -1
  9. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/__init__.py +1 -1
  10. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/_pool.py +328 -89
  11. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/_runtime.py +13 -2
  12. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/log_artifacts.py +3 -1
  13. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/runner.py +44 -6
  14. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/kernel/kernel.py +6 -3
  15. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/mcp/server.py +5 -2
  16. stata_code-0.9.1/tests/test_bugfix_regressions.py +556 -0
  17. stata_code-0.9.1/tests/test_mcp_kernel_extra.py +1087 -0
  18. stata_code-0.9.1/tests/test_pool_refs_extra.py +993 -0
  19. stata_code-0.9.1/tests/test_runner_helpers.py +1121 -0
  20. {stata_code-0.9.0 → stata_code-0.9.1}/LICENSE +0 -0
  21. {stata_code-0.9.0 → stata_code-0.9.1}/LICENSE-POLICY.md +0 -0
  22. {stata_code-0.9.0 → stata_code-0.9.1}/docs/design/hard_timeout.md +0 -0
  23. {stata_code-0.9.0 → stata_code-0.9.1}/docs/industry-leader-roadmap.md +0 -0
  24. {stata_code-0.9.0 → stata_code-0.9.1}/examples/01-basic-regression.md +0 -0
  25. {stata_code-0.9.0 → stata_code-0.9.1}/examples/02-did-card-krueger.md +0 -0
  26. {stata_code-0.9.0 → stata_code-0.9.1}/examples/03-graphs.md +0 -0
  27. {stata_code-0.9.0 → stata_code-0.9.1}/examples/04-multi-session.md +0 -0
  28. {stata_code-0.9.0 → stata_code-0.9.1}/examples/05-large-matrix.md +0 -0
  29. {stata_code-0.9.0 → stata_code-0.9.1}/examples/06-cross-stack-parity-audit.md +0 -0
  30. {stata_code-0.9.0 → stata_code-0.9.1}/examples/07-data-mcp-handoff.md +0 -0
  31. {stata_code-0.9.0 → stata_code-0.9.1}/examples/README.md +0 -0
  32. {stata_code-0.9.0 → stata_code-0.9.1}/schema/run_result.schema.json +0 -0
  33. {stata_code-0.9.0 → stata_code-0.9.1}/scripts/build_skill_zip.py +0 -0
  34. {stata_code-0.9.0 → stata_code-0.9.1}/scripts/check_github_actions.py +0 -0
  35. {stata_code-0.9.0 → stata_code-0.9.1}/scripts/check_versions.py +0 -0
  36. {stata_code-0.9.0 → stata_code-0.9.1}/scripts/export_schema.py +0 -0
  37. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/__main__.py +0 -0
  38. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/cli.py +0 -0
  39. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/__init__.py +0 -0
  40. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/_refs.py +0 -0
  41. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/errors.py +0 -0
  42. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/estimation.py +0 -0
  43. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/handoff.py +0 -0
  44. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/notebook.py +0 -0
  45. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/provenance.py +0 -0
  46. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/run_index.py +0 -0
  47. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/core/schema.py +0 -0
  48. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/doctor.py +0 -0
  49. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/kernel/__init__.py +0 -0
  50. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/kernel/__main__.py +0 -0
  51. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/kernel/assets/logo-32x32.png +0 -0
  52. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/kernel/assets/logo-64x64.png +0 -0
  53. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/kernel/assets/logo-svg.svg +0 -0
  54. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/mcp/__init__.py +0 -0
  55. {stata_code-0.9.0 → stata_code-0.9.1}/stata_code/mcp/__main__.py +0 -0
  56. {stata_code-0.9.0 → stata_code-0.9.1}/tests/__init__.py +0 -0
  57. {stata_code-0.9.0 → stata_code-0.9.1}/tests/conftest.py +0 -0
  58. {stata_code-0.9.0 → stata_code-0.9.1}/tests/fixtures/.gitkeep +0 -0
  59. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_cancel.py +0 -0
  60. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_doctor.py +0 -0
  61. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_errors.py +0 -0
  62. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_estimation.py +0 -0
  63. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_github_actions.py +0 -0
  64. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_handoff.py +0 -0
  65. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_kernel.py +0 -0
  66. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_log_artifacts.py +0 -0
  67. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_mcp.py +0 -0
  68. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_mcp_stdio.py +0 -0
  69. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_method_prompts.py +0 -0
  70. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_new_tools.py +0 -0
  71. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_notebook.py +0 -0
  72. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_notebook_phase2.py +0 -0
  73. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_pool.py +0 -0
  74. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_provenance.py +0 -0
  75. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_public_api.py +0 -0
  76. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_real_stata.py +0 -0
  77. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_release_versions.py +0 -0
  78. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_run_index.py +0 -0
  79. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_runner.py +0 -0
  80. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_runtime_discovery.py +0 -0
  81. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_schema.py +0 -0
  82. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_schema_artifact.py +0 -0
  83. {stata_code-0.9.0 → stata_code-0.9.1}/tests/test_skill_package.py +0 -0
@@ -238,3 +238,5 @@ stata_code_test_export.png
238
238
 
239
239
  # Demo / scratch notebooks (real tests live under tests/)
240
240
  demo-tests/*.ipynb
241
+
242
+ 社媒文件/
@@ -4,6 +4,152 @@ All notable changes to `stata-code` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project adheres
5
5
  to semver-major.minor for the result schema (see `SCHEMA.md` §6).
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## 0.9.1 — 2026-07-22
10
+
11
+ Quality-hardening pass: correctness fixes from an adversarial code review,
12
+ a large offline test expansion, and a documentation accuracy audit. No new
13
+ features, no API or schema changes.
14
+
15
+ A second review round found that three of the first round's fixes were
16
+ incomplete or had introduced a regression of their own; those are the first
17
+ four entries below. The common failure was testing a fix in isolation while
18
+ the real call path stayed uncovered — the new tests go through `SessionPool`
19
+ rather than `WorkerProcess` wherever the pool is what carries the bug, and
20
+ each was confirmed to fail against the pre-fix source.
21
+
22
+ ### Fixed
23
+
24
+ - **A read-only status query no longer kills a healthy mid-run worker.**
25
+ Once `send_simple_op` learned to bail out rather than block on the worker
26
+ lock, that bail-out surfaced as a `_WorkerTimeout` — and
27
+ `SessionPool.stata_info` treated "busy" and "dead" identically: it
28
+ SIGTERMed the worker and dropped it from the pool. Calling the
29
+ `stata_info` tool during a long `bootstrap`/`reghdfe` therefore returned
30
+ the user's run as `adapter_crash` and wiped the session's loaded data, and
31
+ because the MCP layer swallows the exception the kill was invisible in the
32
+ tool response. Busy is now a distinct `_WorkerBusy` signal (a
33
+ `_WorkerTimeout` subclass, so warn-only callers are unaffected) that never
34
+ kills.
35
+ - **A timed-out read no longer steals the next request's response.** Each
36
+ request spawned its own `readline()` thread and abandoned it on deadline
37
+ overrun; the orphan stayed blocked on the pipe and consumed the *following*
38
+ response. The next `execute()` then burned its entire timeout (default
39
+ 600 s) waiting for a reply that had already been eaten, after which the
40
+ pool killed a perfectly healthy worker. Reads now come off one long-lived
41
+ pump thread per worker, which cannot orphan, and responses carrying a
42
+ stale request id are discarded rather than mis-matched.
43
+ - **`send_simple_op` honors its full timeout budget.** The read deadline was
44
+ computed *after* acquiring the worker lock, so a call that spent most of
45
+ its budget waiting for the lock got a second full budget for the read —
46
+ worst case 2x `timeout_ms`, and `list_session_info_detailed` iterates
47
+ workers serially, so N busy workers stalled the status tool for
48
+ `2 * timeout * N`. The deadline is now fixed up front.
49
+ - **Repeated identical notes are no longer double-counted.** The previous
50
+ de-dup short-circuited before recording the matched span, so the second and
51
+ later copies of an identical `note:` line went unclaimed and were picked up
52
+ again by the generic-note pass — the exact double-count the fix was meant
53
+ to remove, reachable from any `foreach` loop re-running the same
54
+ regression.
55
+ - **`stata_info`'s worker eviction is identity-checked.** It popped the
56
+ session's worker unconditionally, so a concurrent `execute()` that had
57
+ already noticed the death and registered a replacement got *its* worker
58
+ evicted — leaking a live pystata subprocess that `request_cancel` /
59
+ `kill_session` / `shutdown` could no longer reach.
60
+ - **`kill()`'s already-dead fast path is identity-checked** too, matching its
61
+ own slow path: it could otherwise clear a handle a concurrent `execute()`
62
+ had just respawned into, stranding a live subprocess mid-request.
63
+ - **`TypeError` is no longer blanket-classified as a caller error.** The
64
+ guard wrapped the whole `execute()` call, not just argument binding, so a
65
+ `TypeError` raised deep inside result collection told the agent its
66
+ arguments were wrong when nothing was — and routed the failure out of the
67
+ `adapter_crash` path that would have recycled the wedged worker. Malformed
68
+ options are now caught by signature binding before the call; deeper
69
+ `TypeError`s stay in the `worker_error` bucket.
70
+ - **Session ids longer than Stata's 32-character name cap are mapped.** They
71
+ were passed through verbatim as a frame name, so `frame create` failed with
72
+ rc 198; they now route through the hashed mapped-frame path.
73
+
74
+ - **Worker stderr pipe is now drained continuously.** The subprocess pool
75
+ only read a worker's stderr after death, so a worker whose cumulative
76
+ stderr output (pystata banners, Stata C-side messages, warnings) exceeded
77
+ the ~64 KB OS pipe buffer blocked mid-write and never responded — the
78
+ parent then misreported the healthy worker as a `timeout` and killed it
79
+ (or hung forever with `timeout_ms=None`). A per-worker daemon thread now
80
+ drains stderr into a bounded tail buffer, which also enriches crash
81
+ messages.
82
+ - **A worker-reported failure no longer destroys the session.** A live
83
+ worker answering `{ok:false, error_kind:"worker_error"}` was handled like
84
+ a dead process: killed and dropped from the pool, wiping the session's
85
+ loaded dataset and `r()`/`e()` state — reachable from a mere argument
86
+ type typo. Such failures now surface as structured errors while the
87
+ worker (and the session's data) survives; the worker also classifies
88
+ `TypeError` as `invalid_request`.
89
+ - **LRU eviction skips busy workers.** `last_used` is stale for a mid-run
90
+ worker, so capacity pressure from an unrelated new session preferred the
91
+ longest-running request as its eviction victim (SIGTERM mid-run →
92
+ `adapter_crash`, session data lost). Eviction now skips in-flight
93
+ workers, bumps `last_used` at request start, and kills victims outside
94
+ the pool lock; timeout/error cleanup no longer removes a newer worker
95
+ handle registered in the interim.
96
+ - **Status queries no longer hang behind a long run.** `send_simple_op`
97
+ blocked unboundedly on the worker lock held by an in-flight `execute()`;
98
+ lock acquisition now counts against the same timeout budget and surfaces
99
+ as a per-session "busy" warning.
100
+ - **`rc` is taken from the last `r(NNN);` in a failure transcript.** An
101
+ earlier successful command echoing a literal `r(NNN);` (display string,
102
+ help output) previously hijacked the return code — and therefore the
103
+ `ErrorKind` classification, suggestions, and recovery contract.
104
+ - **Indented notes are no longer double-counted as warnings.** An indented
105
+ `note: … omitted because of collinearity` line produced both an
106
+ `omitted_collinear` and a generic `note` warning.
107
+ - **A session literally named `"default"` no longer aliases `"main"`.** In
108
+ in-process mode it silently shared `main`'s Stata frame (one dataset for
109
+ two nominally distinct sessions); it now routes through the private
110
+ mapped-frame path.
111
+ - **Matrices with missing sfi row/col names are no longer dropped.**
112
+ Positional names (`r1…`/`c1…`) are synthesized instead of letting the
113
+ shape validator silently discard successfully read values.
114
+ - **`get_graph(format=…)` is no longer a silent no-op.** Requesting a
115
+ format different from the stored one now returns `invalid_request`
116
+ instead of returning mismatched bytes.
117
+ - **Post-run file snapshots survive escaping symlinks.** A symlink inside
118
+ the working dir pointing outside it crashed `changed_output_files` with
119
+ an uncaught `ValueError`; it is now skipped.
120
+
121
+ ### Added
122
+
123
+ - **≈300 new offline tests** (467 → 724 passing without Stata); coverage
124
+ 73% → 90% overall (`mcp/server` 74→99%, `kernel` 73→98%, `_pool` 68→96%,
125
+ `_refs` 68→100%, `log_artifacts` 76→96%, `runner` 29→72%). The package is
126
+ now fully mypy-clean, including the previously unchecked top-level
127
+ modules.
128
+ - **Community health files**: `CONTRIBUTING.md` (dev setup + the exact CI
129
+ gates), `CODE_OF_CONDUCT.md` (Contributor Covenant 2.1), `SECURITY.md`,
130
+ and GitHub issue templates that ask for `stata-code doctor` output.
131
+ - **Second-round regression tests** for the eight fixes listed above, each
132
+ verified to fail against the pre-fix source (823 passing locally with
133
+ Stata). They drive real subprocess workers through `SessionPool` — a
134
+ status query racing a live run, a worker that drops its first request, a
135
+ worker that releases the lock partway — rather than asserting on a mocked
136
+ seam, which is what let the first round's regressions through.
137
+
138
+ ### Docs
139
+
140
+ - Regenerated the skill's `rc → kind` reference from `errors.py` — it still
141
+ documented the pre-0.9.0 mappings the taxonomy audit had corrected; fixed
142
+ the "32 kinds" claims (the enum has 31).
143
+ - `plugin.json` install verify no longer uses `stata-code-mcp --help`
144
+ (which hangs); it runs `stata-code doctor --no-stata-probe`.
145
+ - PUBLISHING.md / AGENTS.md version-bump lists now cover all eight version
146
+ literals the release gate checks; removed the stale claim that a built
147
+ `.vsix` ships in the repo.
148
+ - SCHEMA.md documents `EstimationResult.command_family` and `.diagnostics`;
149
+ status lines updated v0.8 → v0.9; README.zh.md re-synced with the English
150
+ README (plugin-marketplace install, per-client MCP config table, Open VSX
151
+ and first-activation notes, cell/section conventions).
152
+
7
153
  ## 0.9.0 — 2026-06-23
8
154
 
9
155
  ### Fixed
@@ -226,8 +372,8 @@ to semver-major.minor for the result schema (see `SCHEMA.md` §6).
226
372
 
227
373
  ### Added
228
374
 
229
- - **Claude Code plugin marketplace manifest.** `.claude-plugin/marketplace.json`
230
- + `.claude-plugin/plugin.json` expose the repo as a single-plugin
375
+ - **Claude Code plugin marketplace manifest.** `.claude-plugin/marketplace.json` +
376
+ `.claude-plugin/plugin.json` expose the repo as a single-plugin
231
377
  marketplace, so users can install everything (MCP server config + agent
232
378
  skill) with `claude plugin marketplace add brycewang-stanford/stata-code`
233
379
  followed by `claude plugin install stata-code`.
@@ -668,6 +814,9 @@ Aggregated from the prior `Unreleased` section; covers 0.6.1 and 0.6.2.
668
814
  `vscode/package.json`, and the VSCode MCP-client handshake all
669
815
  declare `0.3.0`.
670
816
 
817
+ - **MCP server tool count is now 8** (added `get_matrix`,
818
+ `cancel_session`).
819
+
671
820
  ### Added
672
821
 
673
822
  - **VSCode extension v0.3 — full UI surface** (`vscode/`). Beyond the
@@ -750,11 +899,6 @@ Aggregated from the prior `Unreleased` section; covers 0.6.1 and 0.6.2.
750
899
  in-process and has no clean cancel primitive). Hard interruption
751
900
  remains deferred to the subprocess-based runtime planned for v0.3+.
752
901
 
753
- ### Changed
754
-
755
- - **MCP server tool count is now 8** (added `get_matrix`,
756
- `cancel_session`).
757
-
758
902
  ## [0.2.0] — 2026-05-07
759
903
 
760
904
  The first release that actually ships an end-to-end Stata pipeline. The v1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stata-code
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Agent-native Stata bridge — one core, multiple frontends (MCP, Jupyter, VSCode)
5
5
  Project-URL: Homepage, https://github.com/brycewang-stanford/stata-code
6
6
  Project-URL: Repository, https://github.com/brycewang-stanford/stata-code
@@ -118,7 +118,7 @@ Then just ask:
118
118
  └─────────────┘ └────────────┘ └─────────────────┘
119
119
  ```
120
120
 
121
- **Status: v0.8 (June 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP. The test suite covers schema, runner, MCP, kernel, notebook, run-index, subprocess-pool, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
121
+ **Status: v0.9 (June 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP. The test suite covers schema, runner, MCP, kernel, notebook, run-index, subprocess-pool, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
122
122
 
123
123
  Three workflows the current tree explicitly supports for end users and agents:
124
124
 
@@ -565,10 +565,17 @@ The `stata_required` marker tags the real-Stata integration tests. CI uses `pyte
565
565
 
566
566
  ## Contributing
567
567
 
568
+ Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for the
569
+ development setup, the checks CI runs, and PR guidelines. In short:
570
+
568
571
  - Read [LICENSE-POLICY.md](LICENSE-POLICY.md) before opening a PR.
569
572
  - Add a one-line acknowledgement to your first PR description; the template is in the policy file.
570
573
  - Tests are required for any new schema field or runner behavior.
571
574
 
575
+ Bug reports and support questions go through
576
+ [GitHub issues](https://github.com/brycewang-stanford/stata-code/issues);
577
+ security reports go through [SECURITY.md](SECURITY.md).
578
+
572
579
  ---
573
580
 
574
581
  ## License
@@ -20,12 +20,12 @@ Trusted Publishing has two modes:
20
20
  - **Pending publisher** (preferred for the first release of a brand-new
21
21
  project): you configure the trusted publisher *before* the project exists
22
22
  on PyPI, and the first successful publish creates it. Go to
23
- https://pypi.org/manage/account/publishing/ and add a "pending publisher"
23
+ <https://pypi.org/manage/account/publishing/> and add a "pending publisher"
24
24
  with the values listed in step 2.
25
25
  - **Existing project**: if you already pushed an initial release manually
26
26
  (e.g. via `twine upload` with a one-off API token), configure trusted
27
27
  publishing on the project page at
28
- https://pypi.org/manage/project/stata-code/settings/publishing/.
28
+ <https://pypi.org/manage/project/stata-code/settings/publishing/>.
29
29
 
30
30
  Either path lands you in the same place after the first run.
31
31
 
@@ -76,30 +76,39 @@ The environment doesn't need any secrets — Trusted Publishing handles auth.
76
76
 
77
77
  Once the one-time setup above is done, every release is just:
78
78
 
79
- 1. **Bump the version everywhere**:
79
+ 1. **Bump the version everywhere** (eight literals across six files —
80
+ `scripts/check_versions.py` gates the release build on all of them):
80
81
  - `pyproject.toml` → `[project] version`
81
82
  - `stata_code/__init__.py` → `__version__`
82
83
  - `stata_code/mcp/server.py` → `__version__`
83
84
  - `vscode/package.json` → `version`
84
- - `vscode/package-lock.json` → root package `version`
85
+ - `vscode/package-lock.json` → top-level `version` **and** `packages[""].version`
86
+ - `.claude-plugin/plugin.json` → `version`
87
+ - `.claude-plugin/marketplace.json` → `metadata.version` **and** each `plugins[*].version`
85
88
  2. **Run the version guard** before tagging:
89
+
86
90
  ```bash
87
91
  python scripts/check_versions.py
88
92
  ```
93
+
89
94
  3. **Update `CHANGELOG.md`**: move the `[Unreleased]` entries under a new
90
95
  `## [X.Y.Z] - YYYY-MM-DD` heading, leaving an empty `[Unreleased]` shell
91
96
  on top.
92
97
  4. **Commit** the bump:
98
+
93
99
  ```bash
94
100
  git add pyproject.toml stata_code/__init__.py stata_code/mcp/server.py vscode/package.json vscode/package-lock.json CHANGELOG.md
95
101
  git commit -m "release: vX.Y.Z"
96
102
  ```
103
+
97
104
  5. **Tag and push**:
105
+
98
106
  ```bash
99
107
  git tag vX.Y.Z
100
108
  git push origin main
101
109
  git push origin vX.Y.Z
102
110
  ```
111
+
103
112
  6. Watch the **`release` workflow** under the Actions tab. It will:
104
113
  - build the sdist + wheel
105
114
  - run `twine check` on the artifacts
@@ -127,14 +136,14 @@ version if a publish has already succeeded under that number.
127
136
 
128
137
  ## Manual / emergency publish
129
138
 
130
- `release.yml` also accepts `workflow_dispatch`, so you can trigger a build
131
- + publish from the Actions UI without pushing a tag. The `github-release`
139
+ `release.yml` also accepts `workflow_dispatch`, so you can trigger a build +
140
+ publish from the Actions UI without pushing a tag. The `github-release`
132
141
  job will fail in that mode (no tag to attach to) — that's fine; the PyPI
133
142
  publish itself will have already happened in `publish-pypi`.
134
143
 
135
144
  If Trusted Publishing is broken for some reason and you need an emergency
136
145
  release, generate a short-lived API token at
137
- https://pypi.org/manage/account/token/ and run locally:
146
+ <https://pypi.org/manage/account/token/> and run locally:
138
147
 
139
148
  ```bash
140
149
  python -m build
@@ -79,7 +79,7 @@ Then just ask:
79
79
  └─────────────┘ └────────────┘ └─────────────────┘
80
80
  ```
81
81
 
82
- **Status: v0.8 (June 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP. The test suite covers schema, runner, MCP, kernel, notebook, run-index, subprocess-pool, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
82
+ **Status: v0.9 (June 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP. The test suite covers schema, runner, MCP, kernel, notebook, run-index, subprocess-pool, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
83
83
 
84
84
  Three workflows the current tree explicitly supports for end users and agents:
85
85
 
@@ -526,10 +526,17 @@ The `stata_required` marker tags the real-Stata integration tests. CI uses `pyte
526
526
 
527
527
  ## Contributing
528
528
 
529
+ Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for the
530
+ development setup, the checks CI runs, and PR guidelines. In short:
531
+
529
532
  - Read [LICENSE-POLICY.md](LICENSE-POLICY.md) before opening a PR.
530
533
  - Add a one-line acknowledgement to your first PR description; the template is in the policy file.
531
534
  - Tests are required for any new schema field or runner behavior.
532
535
 
536
+ Bug reports and support questions go through
537
+ [GitHub issues](https://github.com/brycewang-stanford/stata-code/issues);
538
+ security reports go through [SECURITY.md](SECURITY.md).
539
+
533
540
  ---
534
541
 
535
542
  ## License
@@ -360,6 +360,7 @@ Stata's `r()` and `e()` return dictionaries, structurally separated. Each follow
360
360
  | Field | Type | Notes |
361
361
  | --- | --- | --- |
362
362
  | `command` | `string \| null` | Mirrors `e(cmd)` when available; falls back to `last_estimation_cmd`. |
363
+ | `command_family` | `string \| null` | Coarse estimator family derived from the command name (`ols` / `iv` / `gmm` / `panel` / `count` / `did` / …); `null` when the command is unrecognized. |
363
364
  | `depvar` | `string \| null` | Mirrors `e(depvar)`. |
364
365
  | `n_obs` | `int \| null` | Integer form of `e(N)` when available. |
365
366
  | `df_model` | `number \| null` | Mirrors `e(df_m)`. |
@@ -369,6 +370,7 @@ Stata's `r()` and `e()` return dictionaries, structurally separated. Each follow
369
370
  | `ci_level` | `number` | Confidence level used for `ci_low` / `ci_high`; currently `95.0`. |
370
371
  | `coefficients` | `array<Coefficient>` | One row per term in `e(b)`. |
371
372
  | `model_stats` | `dict<str, number \| null>` | High-signal subset of `e()` scalars such as `N`, `df_m`, `df_r`, `r2`, `F`, `chi2`, `ll`, and `rmse`. Full scalars remain under `results.e.scalars`. |
373
+ | `diagnostics` | `dict<str, number \| null>` | Command-aware identification/specification statistics surfaced from `e()` (e.g. weak-ID F and Hansen J for `ivreg2`/`ivreghdfe`, AR(2)/Hansen for `xtabond2`, within-R² for `reghdfe`, `rho` for `xtreg`). Only scalars actually present in `e()` appear — never fabricated. |
372
374
 
373
375
  **`Coefficient` shape:**
374
376
 
@@ -693,7 +695,7 @@ This section tracks how much of the schema is wired up in code. Not normative
693
695
  `png` / `svg` / `pdf`; PNG `width` / `height` parsed from IHDR;
694
696
  best-effort `source_command` / `source_line` attribution from the
695
697
  submitted code. `inline` populated when `include_graphs="inline"`.
696
- - Structured `error` — 32-kind enum, `varname` / `path` / `name`
698
+ - Structured `error` — 31-kind enum, `varname` / `path` / `name`
697
699
  extracted from Stata's English error text by regex, structured
698
700
  `context` (`{before, failing, after}`), `commands_executed` parsed
699
701
  from pystata's multi-line transcript, `suggestions` generated by
@@ -144,8 +144,8 @@ remaining · ⬜ not started.
144
144
  with structured results + MCP" converged across StatsPAI and rmcp. Defend by
145
145
  positioning on **authoritative Stata-runtime fidelity**, not breadth.
146
146
  - **Generic code-exec substitution.** A Jupyter-MCP + statsmodels sandbox can
147
- "do econometrics" with zero domain tooling. Defense: *mandated Stata commands
148
- + verifiable typed `e()` contracts* a generic sandbox cannot provide.
147
+ "do econometrics" with zero domain tooling. Defense: *mandated Stata commands +
148
+ verifiable typed `e()` contracts* a generic sandbox cannot provide.
149
149
  - **License contagion.** The two structured/MCP-native competitors (tmonk,
150
150
  SepineTam) are AGPL; the kernels are GPL. To preserve our MIT clean-room
151
151
  wedge we must never vendor their code paths (see `LICENSE-POLICY.md`).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "stata-code"
7
- version = "0.9.0"
7
+ version = "0.9.1"
8
8
  description = "Agent-native Stata bridge — one core, multiple frontends (MCP, Jupyter, VSCode)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -195,7 +195,7 @@ def is_available() -> bool:
195
195
  return True
196
196
 
197
197
 
198
- __version__ = "0.9.0"
198
+ __version__ = "0.9.1"
199
199
 
200
200
  __all__ = [
201
201
  # Primary entry points