stata-code 0.11.0__tar.gz → 0.12.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 (100) hide show
  1. {stata_code-0.11.0 → stata_code-0.12.0}/CHANGELOG.md +81 -0
  2. {stata_code-0.11.0 → stata_code-0.12.0}/PKG-INFO +124 -14
  3. {stata_code-0.11.0 → stata_code-0.12.0}/README.en.md +123 -13
  4. {stata_code-0.11.0 → stata_code-0.12.0}/README.md +168 -11
  5. {stata_code-0.11.0 → stata_code-0.12.0}/SCHEMA.md +1 -1
  6. {stata_code-0.11.0 → stata_code-0.12.0}/docs/quickstart.zh.md +25 -1
  7. {stata_code-0.11.0 → stata_code-0.12.0}/pyproject.toml +1 -1
  8. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/__init__.py +1 -1
  9. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/cli.py +264 -3
  10. stata_code-0.12.0/stata_code/core/daemon.py +570 -0
  11. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/runner.py +5 -22
  12. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/mcp/server.py +1 -1
  13. stata_code-0.12.0/tests/test_daemon.py +423 -0
  14. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_runner.py +12 -1
  15. {stata_code-0.11.0 → stata_code-0.12.0}/.gitignore +0 -0
  16. {stata_code-0.11.0 → stata_code-0.12.0}/LICENSE +0 -0
  17. {stata_code-0.11.0 → stata_code-0.12.0}/LICENSE-POLICY.md +0 -0
  18. {stata_code-0.11.0 → stata_code-0.12.0}/PUBLISHING.md +0 -0
  19. {stata_code-0.11.0 → stata_code-0.12.0}/docs/competitive-landscape.md +0 -0
  20. {stata_code-0.11.0 → stata_code-0.12.0}/docs/design/hard_timeout.md +0 -0
  21. {stata_code-0.11.0 → stata_code-0.12.0}/docs/industry-leader-roadmap.md +0 -0
  22. {stata_code-0.11.0 → stata_code-0.12.0}/examples/01-basic-regression.md +0 -0
  23. {stata_code-0.11.0 → stata_code-0.12.0}/examples/02-did-card-krueger.md +0 -0
  24. {stata_code-0.11.0 → stata_code-0.12.0}/examples/03-graphs.md +0 -0
  25. {stata_code-0.11.0 → stata_code-0.12.0}/examples/04-multi-session.md +0 -0
  26. {stata_code-0.11.0 → stata_code-0.12.0}/examples/05-large-matrix.md +0 -0
  27. {stata_code-0.11.0 → stata_code-0.12.0}/examples/06-cross-stack-parity-audit.md +0 -0
  28. {stata_code-0.11.0 → stata_code-0.12.0}/examples/07-data-mcp-handoff.md +0 -0
  29. {stata_code-0.11.0 → stata_code-0.12.0}/examples/README.md +0 -0
  30. {stata_code-0.11.0 → stata_code-0.12.0}/schema/run_result.schema.json +0 -0
  31. {stata_code-0.11.0 → stata_code-0.12.0}/scripts/build_skill_zip.py +0 -0
  32. {stata_code-0.11.0 → stata_code-0.12.0}/scripts/build_standalone.py +0 -0
  33. {stata_code-0.11.0 → stata_code-0.12.0}/scripts/check_github_actions.py +0 -0
  34. {stata_code-0.11.0 → stata_code-0.12.0}/scripts/check_versions.py +0 -0
  35. {stata_code-0.11.0 → stata_code-0.12.0}/scripts/export_schema.py +0 -0
  36. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/__main__.py +0 -0
  37. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/__init__.py +0 -0
  38. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/_pool.py +0 -0
  39. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/_refs.py +0 -0
  40. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/_runtime.py +0 -0
  41. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/console.py +0 -0
  42. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/errors.py +0 -0
  43. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/estimation.py +0 -0
  44. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/handoff.py +0 -0
  45. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/jobs.py +0 -0
  46. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/lint.py +0 -0
  47. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/log_artifacts.py +0 -0
  48. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/notebook.py +0 -0
  49. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/policy.py +0 -0
  50. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/provenance.py +0 -0
  51. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/run_index.py +0 -0
  52. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/core/schema.py +0 -0
  53. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/doctor.py +0 -0
  54. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/kernel/__init__.py +0 -0
  55. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/kernel/__main__.py +0 -0
  56. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/kernel/assets/logo-32x32.png +0 -0
  57. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/kernel/assets/logo-64x64.png +0 -0
  58. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/kernel/assets/logo-svg.svg +0 -0
  59. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/kernel/kernel.py +0 -0
  60. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/mcp/__init__.py +0 -0
  61. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/mcp/__main__.py +0 -0
  62. {stata_code-0.11.0 → stata_code-0.12.0}/stata_code/mcp_setup.py +0 -0
  63. {stata_code-0.11.0 → stata_code-0.12.0}/tests/__init__.py +0 -0
  64. {stata_code-0.11.0 → stata_code-0.12.0}/tests/conftest.py +0 -0
  65. {stata_code-0.11.0 → stata_code-0.12.0}/tests/fixtures/.gitkeep +0 -0
  66. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_agent_ergonomics.py +0 -0
  67. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_benchmark.py +0 -0
  68. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_bugfix_regressions.py +0 -0
  69. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_cancel.py +0 -0
  70. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_cli.py +0 -0
  71. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_console.py +0 -0
  72. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_doctor.py +0 -0
  73. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_errors.py +0 -0
  74. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_estimation.py +0 -0
  75. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_github_actions.py +0 -0
  76. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_handoff.py +0 -0
  77. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_kernel.py +0 -0
  78. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_lint.py +0 -0
  79. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_lint_mcp.py +0 -0
  80. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_log_artifacts.py +0 -0
  81. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_mcp.py +0 -0
  82. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_mcp_kernel_extra.py +0 -0
  83. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_mcp_transport.py +0 -0
  84. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_method_prompts.py +0 -0
  85. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_new_tools.py +0 -0
  86. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_notebook.py +0 -0
  87. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_notebook_phase2.py +0 -0
  88. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_policy.py +0 -0
  89. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_pool.py +0 -0
  90. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_pool_refs_extra.py +0 -0
  91. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_provenance.py +0 -0
  92. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_public_api.py +0 -0
  93. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_real_stata.py +0 -0
  94. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_release_versions.py +0 -0
  95. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_run_index.py +0 -0
  96. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_runner_helpers.py +0 -0
  97. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_runtime_discovery.py +0 -0
  98. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_schema.py +0 -0
  99. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_schema_artifact.py +0 -0
  100. {stata_code-0.11.0 → stata_code-0.12.0}/tests/test_skill_package.py +0 -0
@@ -6,6 +6,87 @@ to semver-major.minor for the result schema (see `SCHEMA.md` §6).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 0.12.0 — 2026-07-28
10
+
11
+ Adds a resident session daemon so the CLI can keep data in memory across
12
+ invocations, and fixes a VS Code regression introduced by 0.11.0's payload
13
+ defaults.
14
+
15
+ ### Added
16
+
17
+ #### Resident session daemon — state across CLI invocations
18
+
19
+ The subprocess pool already kept one Stata worker alive per session, but a plain
20
+ `stata-code run` owned that pool and tore it down on exit, so two consecutive CLI
21
+ calls could not share data in memory. The new `daemon` subcommand holds the pool
22
+ in a resident process behind a Unix socket:
23
+
24
+ ```bash
25
+ stata-code run --daemon -e 'sysuse auto, clear' -e 'gen z = price/1000'
26
+ stata-code run --daemon -e 'summarize z' # new process, z is still loaded
27
+ ```
28
+
29
+ - **`stata-code run --daemon`** routes the run through the daemon, starting it on
30
+ demand. `--socket` overrides the path. Graph refs are resolved back over the
31
+ socket, so `--graphs DIR` keeps working.
32
+ - **`stata-code daemon start | stop | restart | status`** for explicit control;
33
+ `status --json` is machine-readable.
34
+ - **Idle retirement** after 30 minutes by default (`--idle-timeout`, `0` disables)
35
+ so a forgotten daemon does not hold a Stata license slot.
36
+ - **Unix socket only, never TCP** — mode 0600 inside a mode-0700 directory. The
37
+ command-safety guard still applies to everything the daemon runs.
38
+ - **Requires the pystata backend.** `--backend console` is stateless batch
39
+ execution, so combining it with `--daemon` is a hard error rather than a silent
40
+ no-op.
41
+ - Falls back to a short hashed `/tmp` socket path when the natural location would
42
+ exceed the platform's ~104-byte `sun_path` limit; every entry point derives the
43
+ same fallback, so clients still find the daemon.
44
+
45
+ #### VS Code Outputs panel now lists what every run wrote
46
+
47
+ The panel read only `log.files.output_paths` — the archived copies inside a run
48
+ bundle — so it stayed empty unless the caller had opted into
49
+ `persist_log_files`. It now also reads `result.outputs`, the files a run wrote
50
+ in its working directory (reported on every run since 0.11.0), shown with their
51
+ size and listed *ahead* of bundle copies, since the working-directory file is
52
+ the one you want to open. Nodes carry an `origin` of `workdir` or `bundle`, and
53
+ an overwritten file says so in its tooltip.
54
+
55
+ ### Fixed
56
+
57
+ - **VS Code: r()/e() matrices displayed as `0x0` and opened empty.** 0.11.0
58
+ made `include_results: "scalars"` the server default, which returns each
59
+ matrix as a `matrix://` stub with `rows` / `cols` elided (the true shape moved
60
+ to `n_rows` / `n_cols`). The extension's sidebar derives its dimension label
61
+ from `rows.length` / `cols.length` and renders `matrix.values` directly, so
62
+ every matrix showed `0x0` and exported an empty table. The extension now
63
+ requests `include_results: "full"` — an editor is a human surface with no
64
+ token budget to defend, matching what the Jupyter kernel already did. The
65
+ argument builder moved to a `vscode`-free `runArgs.ts` so the behaviour is
66
+ covered by unit tests.
67
+ - Documentation: the error taxonomy was described as "32 kinds" in `README.md`,
68
+ `README.en.md`, and `SCHEMA.md` while `ErrorKind` has had 34 members (the
69
+ English README's prose already said 34, contradicting its own table).
70
+ - Documentation: both READMEs listed "Stata 17+" as a hard install requirement,
71
+ which contradicted the console backend's documented Stata 13+ support.
72
+ - `README.md` was missing the command-line, client-setup, and command-safety
73
+ sections that `README.en.md` has had since 0.11.0.
74
+ - Documentation: `SKILL.md` and the agent reference library still described the
75
+ pre-0.11 surface — 18 tools, 31 error kinds, no mention of matrix stubs,
76
+ background runs, `result.outputs`, or `error.source_file`. Counts in every doc
77
+ are now checked against the code.
78
+
79
+ ### Internal
80
+
81
+ - `_collect_returns` no longer takes a `request_id` or creates `matrix://` refs.
82
+ Nothing passed it — neither the production path nor any test — and ref
83
+ creation belongs to `_project_returns`, which owns the payload budget.
84
+ - `test_varname_not_found_emits_did_you_mean` failed once and could not be
85
+ reproduced. Its fuzzy match draws candidates from `dataset.variables`, so a
86
+ lost `auto.dta` in the shared in-process Stata degrades the suggestion for
87
+ reasons unrelated to the matching logic. The assertion was **split**, not
88
+ loosened: a precondition check now reports which of the two it was.
89
+
9
90
  ## 0.11.0 — 2026-07-28
10
91
 
11
92
  An agent-ergonomics release, driven by a report from an agent that used the MCP
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stata-code
3
- Version: 0.11.0
3
+ Version: 0.12.0
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
@@ -120,7 +120,7 @@ Then just ask:
120
120
 
121
121
  A fourth frontend, a **plain-terminal CLI** (`stata-code run` / `lint` / `setup`), gives any agent that can shell out — or a bare terminal — the same typed `RunResult`. And the core runs two backends: **pystata** (Stata 17+, in-memory sessions) or a **console backend** (Stata 13+ in batch mode, no pystata) — both returning the identical schema.
122
122
 
123
- **Status: v0.10 (July 2026)** — the core, MCP server, Jupyter kernel, VS Code extension, and CLI work end-to-end against Stata 18 MP; the console backend broadens coverage to Stata 13+ without pystata. The test suite covers schema, runner, console parser, MCP, kernel, notebook, run-index, subprocess-pool, command policy, linter, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
123
+ **Status: v0.12 (July 2026)** — the core, MCP server, Jupyter kernel, VS Code extension, and CLI work end-to-end against Stata 18 MP; the console backend broadens coverage to Stata 13+ without pystata. v0.12 adds a resident session daemon so consecutive CLI runs share data in memory, on top of v0.11's agent-ergonomics work: bounded result payloads, background execution for multi-minute jobs, error localization inside `do`-files, automatic log-handle cleanup, and generated-file reporting — see the [changelog](CHANGELOG.md). The test suite covers schema, runner, console parser, MCP, kernel, notebook, run-index, subprocess-pool, command policy, linter, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
124
124
 
125
125
  Four workflows the current tree explicitly supports for end users and agents:
126
126
 
@@ -158,7 +158,9 @@ For the project's clean-room policy around AGPL/GPL Stata projects, see [LICENSE
158
158
 
159
159
  ## Install
160
160
 
161
- Requirements: **Stata 17+** (with `pystata` shipped by Stata) and **Python 3.10+**.
161
+ Requirements: **Python 3.10+**, plus either **Stata 17+** (using the `pystata`
162
+ shipped by Stata, for in-memory sessions) or **Stata 13+** (via the console
163
+ backend, which needs no `pystata` and is stateless).
162
164
 
163
165
  ```bash
164
166
  # from PyPI
@@ -264,6 +266,54 @@ CI workflow template at
264
266
  bundles the runtime and needs no Python install. Paired with `--backend console`,
265
267
  it is a fully Python-free path to typed Stata results.
266
268
 
269
+ ### The Session Daemon (State Across Invocations)
270
+
271
+ By default every `stata-code run` starts a fresh process, so in-memory data dies
272
+ with it. The `daemon` subcommand moves the subprocess pool into a resident
273
+ process behind a Unix socket, and consecutive `run --daemon` calls then land in
274
+ the **same Stata session**:
275
+
276
+ ```bash
277
+ stata-code run --daemon -e 'sysuse auto, clear' -e 'gen z = price/1000'
278
+ stata-code run --daemon -e 'summarize z' # new process, z is still there
279
+ stata-code run --daemon -e 'regress price mpg'
280
+ stata-code run --daemon -e 'display e(r2)' # e() from the previous process too
281
+ ```
282
+
283
+ The first `run --daemon` starts the daemon on demand — no manual step needed. You
284
+ can also drive it explicitly:
285
+
286
+ ```bash
287
+ stata-code daemon start # detached (--foreground to stay attached)
288
+ stata-code daemon status # pid, uptime, live sessions
289
+ stata-code daemon status --json
290
+ stata-code daemon stop
291
+ stata-code daemon restart
292
+ stata-code daemon start --idle-timeout 0 # 0 disables idle retirement
293
+ ```
294
+
295
+ Worth knowing:
296
+
297
+ - **Session isolation is unchanged.** `--session other` is still a separate Stata
298
+ frame / worker inside the daemon; sessions cannot see each other's data.
299
+ - **It retires itself after 30 idle minutes** so a forgotten daemon does not sit on
300
+ a Stata license slot. Tune with `--idle-timeout`; `0` disables it.
301
+ - **Unix socket only, never TCP.** The daemon executes arbitrary Stata code, so it
302
+ is deliberately reachable only through a mode-0600 socket inside a mode-0700
303
+ directory. The [command-safety](#command-safety) guard still applies.
304
+ - **Requires the pystata backend.** `--backend console` is stateless batch
305
+ execution by design, so pairing it with a daemon is an error rather than a
306
+ silent no-op.
307
+ - **Long socket paths degrade gracefully.** `sun_path` caps out around 104 bytes;
308
+ when the natural location is longer (a deep home directory, a long
309
+ `XDG_RUNTIME_DIR`), the socket moves to a short `/tmp` path derived from a hash
310
+ of the intended one. Every entry point derives it the same way, so clients still
311
+ find the daemon.
312
+
313
+ Plain `stata-code run`, `run --daemon`, and the MCP server all drive the same
314
+ engine and return the same `RunResult` schema — they differ only in how long the
315
+ Stata session lives.
316
+
267
317
  ### One-Command Client Setup
268
318
 
269
319
  `stata-code setup` writes the MCP server entry into a client's config — the
@@ -513,11 +563,14 @@ The extension still requires the MCP extra on your system Python (`pip install "
513
563
 
514
564
  ## Token-Economy Defaults
515
565
 
516
- A typical `stata_run` response is about **10x smaller** than servers that dump logs and images directly. Three design choices drive this:
566
+ A typical `stata_run` response is about **10x smaller** than servers that dump logs and images directly. Four design choices drive this:
567
+
568
+ 1. **Logs return `head` + `tail` + `ref`** by default. Full logs are fetched on demand via `get_log(ref)`, or grepped in place with `search_log(ref, pattern)`. A Stata regression log can be about 6,000 tokens; `stata-code` returns about 600 by default.
569
+ 2. **Graphs return refs, not inline base64**. A 30 KB PNG can become about 50,000 base64 tokens; returning a ref avoids that unless the agent actually needs the bytes. When they are requested inline, they come back as real MCP image content blocks — viewable by a vision-capable client, rather than base64 buried in a JSON string that costs tokens and shows nothing.
570
+ 3. **Each estimation is described once.** By default (`include_results: "scalars"`) `r()` / `e()` scalars and macros are inline, while every matrix becomes a `matrix://` stub carrying just its shape. Without this, one regression ships the same numbers four times — `e(b)`, `e(V)`'s label lists, `e(beta)`, `r(table)` — on top of `results.estimation`, which already holds the typed coefficient table. A 123-term regression drops from ~57 KB to ~28 KB. Raw values stay one `get_matrix(ref)` away, and `include_results: "full"` restores the old shape.
571
+ 4. **Errors are typed**. Agents can check `err.kind == "varname_not_found"` instead of regex-parsing English logs.
517
572
 
518
- 1. **Logs return `head` + `tail` + `ref`** by default. Full logs are fetched on demand via `get_log(ref)`. A Stata regression log can be about 6,000 tokens; `stata-code` returns about 600 by default.
519
- 2. **Graphs return refs, not inline base64**. A 30 KB PNG can become about 50,000 base64 tokens; returning a ref avoids that unless the agent actually needs the bytes.
520
- 3. **Errors are typed**. Agents can check `err.kind == "varname_not_found"` instead of regex-parsing English logs.
573
+ Two further knobs matter for fixed-effect-heavy work: `include_estimation: "summary"` keeps the model-level block and drops per-term rows, and `max_coefficients` caps the table. Either way `estimation.n_coefficients` reports the model's true size and `coefficients_truncated` flags the cut, so a trimmed table is never mistaken for a smaller model.
521
574
 
522
575
  For example, a misspelled variable returns a structured error:
523
576
 
@@ -529,6 +582,7 @@ For example, a misspelled variable returns a structured error:
529
582
  "kind": "varname_not_found",
530
583
  "varname": "mpgg",
531
584
  "line": 3,
585
+ "source_file": null,
532
586
  "context": {
533
587
  "before": ["use auto"],
534
588
  "failing": "summarize mpgg",
@@ -536,11 +590,19 @@ For example, a misspelled variable returns a structured error:
536
590
  },
537
591
  "suggestions": [
538
592
  {"action": "Did you mean `mpg`?", "command": "describe"}
539
- ]
593
+ ],
594
+ "recovery": {
595
+ "category": "user_code",
596
+ "retriable": false,
597
+ "needs_code_change": true,
598
+ "needs_user_input": false
599
+ }
540
600
  }
541
601
  }
542
602
  ```
543
603
 
604
+ When the failing command lives inside a script you invoked (`do "analysis.do"`), `line` and `context` point *inside that script* and `source_file` names it — so an agent does not have to re-read the file to find the offending line.
605
+
544
606
  The full schema is in [SCHEMA.md](SCHEMA.md).
545
607
 
546
608
  ---
@@ -576,7 +638,7 @@ stata_code/
576
638
  | Standalone MCP | ✓ | ✓ | bundled with VS Code | — (bespoke HTTP + copy-paste) | — |
577
639
  | Jupyter kernel | ✓ | — | — | — | ✓ |
578
640
  | Unified result schema | ✓ ([SCHEMA.md](SCHEMA.md)) | per-tool | per-tool | raw log to the agent | per-tool |
579
- | Typed errors + suggestions | ✓ (32 kinds) | — | — | — | — |
641
+ | Typed errors + suggestions | ✓ (34 kinds) | — | — | — | — |
580
642
  | Token-economy defaults | ✓ (log refs, graph refs) | — | — | — | — |
581
643
  | Command-safety guard | ✓ (`shell`/`erase`/`rmdir`/`!`) | ✓ (27-rule) | — | — | — |
582
644
  | Bash / plain-terminal CLI | ✓ (`stata-code run`) | ✓ | — | — | — |
@@ -590,7 +652,7 @@ stata_code/
590
652
 
591
653
  `stata-code` owns the **agent-native, typed-contract** lane: one structured
592
654
  `RunResult` schema across MCP, Jupyter, VS Code, and a plain-terminal CLI; a
593
- 32-kind error taxonomy with recovery contracts; token-economy refs; and now a
655
+ 34-kind error taxonomy with recovery contracts; token-economy refs; and now a
594
656
  console backend (Stata 13+) plus a zero-Python binary. Editor-first tools like
595
657
  `stata-all-in-one` lead on human IDE polish and hand the agent raw log text;
596
658
  `stata-code` matches their onboarding/version reach while keeping the typed
@@ -598,6 +660,49 @@ execution contract they don't have. See
598
660
  [docs/competitive-landscape.md](docs/competitive-landscape.md) for the full
599
661
  teardown.
600
662
 
663
+ ### Versus calling the Stata CLI directly
664
+
665
+ To be clear up front: **you can drive Stata from a shell with no tooling at all.**
666
+ Most Stata installs ship a command-line executable, and for one-shot execution it
667
+ is perfectly good — no reason to add a dependency:
668
+
669
+ ```bash
670
+ /Applications/Stata/StataMP.app/Contents/MacOS/stata-mp -b do analysis.do
671
+ printf 'sysuse auto, clear\nsummarize mpg\nexit, clear\n' | stata-mp -q
672
+ ```
673
+
674
+ What's worth knowing is where that path breaks down. Both columns below drive the
675
+ **same Stata binary and parse the same log** (`stata-code` via `--backend console`);
676
+ the only difference is the wrapper around it:
677
+
678
+ | | plain `stata-mp -b` | `stata-code run` |
679
+ | --- | --- | --- |
680
+ | Exit code on error | **`0`** — fails silently | `1` |
681
+ | How you learn it failed | grep the log for `r(111);` yourself | `ok=False`, `rc=111` |
682
+ | Error classification | none | `[varname_not_found]` (34 kinds) |
683
+ | Recovery hint | none | "Run `describe` to list available variables" |
684
+ | Getting a coefficient | scrape the aligned ASCII table | `e.scalars["r2"] → 0.6515312529087511` (float) |
685
+ | State across calls | no | `run --daemon` (see below) |
686
+
687
+ The first row is the one that bites: Stata's batch mode **returns exit code 0 even
688
+ when the code errors out**. In an automated loop, an agent sees `exit 0`, moves on,
689
+ and treats a failed run's output as valid.
690
+
691
+ Rule of thumb:
692
+
693
+ - **One-shot execution** ("run this do-file and show me the output") — the plain
694
+ CLI is fine; don't over-engineer it.
695
+ - **Scripts / CI** (anything that branches on success) — use `stata-code run` and
696
+ get a trustworthy signal for near-zero cost.
697
+ - **Iterative analysis** (many rounds, reading intermediate results, deciding the
698
+ next step from `e()`) — use `run --daemon` or MCP; otherwise statelessness and
699
+ text-scraping compound round over round.
700
+
701
+ > Without `--daemon`, `stata-code run` **starts a fresh process per invocation**:
702
+ > multiple `-e` snippets in one call share a session, but in-memory data is gone
703
+ > once the process exits. With `--daemon` the data stays in a resident process —
704
+ > see [the session daemon](#the-session-daemon-state-across-invocations).
705
+
601
706
  ---
602
707
 
603
708
  ## Roadmap
@@ -607,11 +712,12 @@ teardown.
607
712
  - v1.0 result schema ([SCHEMA.md](SCHEMA.md))
608
713
  - `pystata`-based runner with native-typed `r()`, `e()`, and matrices
609
714
  - Multi-session via Stata frames (`session_id` accepts `[A-Za-z0-9_-]+`; ids such as `model-a` are mapped to private legal frame names internally while the public id is echoed back)
715
+ - Resident session daemon (`stata-code daemon`, `run --daemon`) so CLI invocations share one live Stata session over a mode-0600 Unix socket, with idle retirement
610
716
  - Per-line error attribution: line number, context, commands_executed
611
717
  - Graph capture: `png` / `svg` / `pdf` with ref store and source-command attribution
612
718
  - Log truncation with ref store
613
719
  - Warning extraction: 5 categories + generic notes
614
- - 32-kind error taxonomy with canonical suggestions
720
+ - 34-kind error taxonomy with canonical suggestions and a machine-readable `recovery` verdict (retriable / needs-code-change / needs-user-input)
615
721
  - MCP server: 21 tools, including notebook navigation / search / atomic edits, the run-bundle index (`list_runs`), log grep (`search_log`), dataset inspection (`inspect_data`), static linting (`lint_do`), and package installation (`install_package`)
616
722
  - Command-safety guard: OS-escape / file-deletion commands (`shell`, `winexec`, `erase`, `rm`, `rmdir`, `!`) are blocked before Stata runs; configurable via `STATA_CODE_COMMAND_POLICY` / `STATA_CODE_POLICY_ALLOW` / `STATA_CODE_POLICY_BLOCK`
617
723
  - Bash / plain-terminal surface: `stata-code run` (a `.do` file, `-e` snippets, or stdin) prints the same structured `RunResult` any agent that can shell out can consume; `stata-code lint` runs the linter; `stata-code setup` writes MCP client configs
@@ -619,8 +725,12 @@ teardown.
619
725
  - Zero-Python standalone binary ([`scripts/build_standalone.py`](scripts/build_standalone.py) + CI workflow template [`packaging/standalone.github-workflow.yml`](packaging/standalone.github-workflow.yml)); with `--backend console` it is a fully Python-free path to typed results
620
726
  - One-click VS Code onboarding: the extension offers to create a workspace `.venv` and install the server (command palette: “Stata: Set Up MCP Server”)
621
727
  - Jupyter kernel: rewired to the v1.0 pipeline, kernel logos bundled
622
- - Matrix size cap + `get_matrix(ref)` for large matrices (>10k cells)
623
- - Subprocess-backed hard timeout and cancellation for the public Python API and MCP server: `timeout_ms`, `cancel(session_id)`, and MCP `cancel_session`
728
+ - Result-payload budget: `include_results` (matrices become `matrix://` stubs by default), `include_estimation`, and `max_coefficients`, with `get_matrix(ref)` to pull raw values on demand
729
+ - Subprocess-backed hard timeout and cancellation for the public Python API and MCP server: `timeout_ms` (now an advertised `stata_run` argument, and it budgets queueing too — contention returns `rc=-5` / `session_busy` instead of blocking), `cancel(session_id)`, and MCP `cancel_session`
730
+ - Background execution for multi-minute jobs: `run_in_background` returns a job id; `stata_run_status` (with a bounded `wait_ms`) and `list_background_runs` poll it
731
+ - Error localization inside `do`/`run` scripts: `error.line` + `error.context` resolve within the invoked file and `error.source_file` names it; a failed run still carries a full searchable log
732
+ - Log-handle hygiene: handles leaked by a failed run are closed automatically (`auto_close_logs`), so an aborted script cannot make every later run in that session fail with r(604)
733
+ - Generated-file reporting: `result.outputs` lists the tables, exports and datasets each run wrote, independent of the run-bundle options
624
734
  - Per-cell repair loop on `.ipynb` via `notebook_outline` / `notebook_get_cell` / `notebook_edit_cell` with optimistic-concurrency `expected_source` guards and `origin_cell_id` echo on `RunResult`
625
735
  - Persistent run bundles + `list_runs` query over `manifest.json` files (filter by cell / origin / session / since / ok; page with limit / offset)
626
736
  - Read-only `stata-code doctor` / `verify` diagnostics for package version,
@@ -635,7 +745,7 @@ teardown.
635
745
 
636
746
  ### Next Up
637
747
 
638
- - Streaming / progress for long runs (`log.complete:false`, incremental log lines) so 20-minute `boottest` / `csdid` jobs report before they finish
748
+ - Streaming / incremental progress for long runs (`log.complete:false`, partial log lines). v0.11's `run_in_background` already unblocks the caller for 20-minute `boottest` / `csdid` jobs, but a running job still reports nothing until it finishes
639
749
  - Hard timeout / cancellation for the Jupyter kernel (move it from the direct in-process runner to the subprocess pool, or an equivalent)
640
750
  - Console backend: graph capture and richer matrix coverage (values currently materialized for the estimation matrices; state is per-call)
641
751
  - Human IDE polish to match editor-first tools: inline graph rendering + DPI export, a scalable data viewer, and an optional "attach to a running Stata" backend
@@ -81,7 +81,7 @@ Then just ask:
81
81
 
82
82
  A fourth frontend, a **plain-terminal CLI** (`stata-code run` / `lint` / `setup`), gives any agent that can shell out — or a bare terminal — the same typed `RunResult`. And the core runs two backends: **pystata** (Stata 17+, in-memory sessions) or a **console backend** (Stata 13+ in batch mode, no pystata) — both returning the identical schema.
83
83
 
84
- **Status: v0.10 (July 2026)** — the core, MCP server, Jupyter kernel, VS Code extension, and CLI work end-to-end against Stata 18 MP; the console backend broadens coverage to Stata 13+ without pystata. The test suite covers schema, runner, console parser, MCP, kernel, notebook, run-index, subprocess-pool, command policy, linter, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
84
+ **Status: v0.12 (July 2026)** — the core, MCP server, Jupyter kernel, VS Code extension, and CLI work end-to-end against Stata 18 MP; the console backend broadens coverage to Stata 13+ without pystata. v0.12 adds a resident session daemon so consecutive CLI runs share data in memory, on top of v0.11's agent-ergonomics work: bounded result payloads, background execution for multi-minute jobs, error localization inside `do`-files, automatic log-handle cleanup, and generated-file reporting — see the [changelog](CHANGELOG.md). The test suite covers schema, runner, console parser, MCP, kernel, notebook, run-index, subprocess-pool, command policy, linter, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
85
85
 
86
86
  Four workflows the current tree explicitly supports for end users and agents:
87
87
 
@@ -119,7 +119,9 @@ For the project's clean-room policy around AGPL/GPL Stata projects, see [LICENSE
119
119
 
120
120
  ## Install
121
121
 
122
- Requirements: **Stata 17+** (with `pystata` shipped by Stata) and **Python 3.10+**.
122
+ Requirements: **Python 3.10+**, plus either **Stata 17+** (using the `pystata`
123
+ shipped by Stata, for in-memory sessions) or **Stata 13+** (via the console
124
+ backend, which needs no `pystata` and is stateless).
123
125
 
124
126
  ```bash
125
127
  # from PyPI
@@ -225,6 +227,54 @@ CI workflow template at
225
227
  bundles the runtime and needs no Python install. Paired with `--backend console`,
226
228
  it is a fully Python-free path to typed Stata results.
227
229
 
230
+ ### The Session Daemon (State Across Invocations)
231
+
232
+ By default every `stata-code run` starts a fresh process, so in-memory data dies
233
+ with it. The `daemon` subcommand moves the subprocess pool into a resident
234
+ process behind a Unix socket, and consecutive `run --daemon` calls then land in
235
+ the **same Stata session**:
236
+
237
+ ```bash
238
+ stata-code run --daemon -e 'sysuse auto, clear' -e 'gen z = price/1000'
239
+ stata-code run --daemon -e 'summarize z' # new process, z is still there
240
+ stata-code run --daemon -e 'regress price mpg'
241
+ stata-code run --daemon -e 'display e(r2)' # e() from the previous process too
242
+ ```
243
+
244
+ The first `run --daemon` starts the daemon on demand — no manual step needed. You
245
+ can also drive it explicitly:
246
+
247
+ ```bash
248
+ stata-code daemon start # detached (--foreground to stay attached)
249
+ stata-code daemon status # pid, uptime, live sessions
250
+ stata-code daemon status --json
251
+ stata-code daemon stop
252
+ stata-code daemon restart
253
+ stata-code daemon start --idle-timeout 0 # 0 disables idle retirement
254
+ ```
255
+
256
+ Worth knowing:
257
+
258
+ - **Session isolation is unchanged.** `--session other` is still a separate Stata
259
+ frame / worker inside the daemon; sessions cannot see each other's data.
260
+ - **It retires itself after 30 idle minutes** so a forgotten daemon does not sit on
261
+ a Stata license slot. Tune with `--idle-timeout`; `0` disables it.
262
+ - **Unix socket only, never TCP.** The daemon executes arbitrary Stata code, so it
263
+ is deliberately reachable only through a mode-0600 socket inside a mode-0700
264
+ directory. The [command-safety](#command-safety) guard still applies.
265
+ - **Requires the pystata backend.** `--backend console` is stateless batch
266
+ execution by design, so pairing it with a daemon is an error rather than a
267
+ silent no-op.
268
+ - **Long socket paths degrade gracefully.** `sun_path` caps out around 104 bytes;
269
+ when the natural location is longer (a deep home directory, a long
270
+ `XDG_RUNTIME_DIR`), the socket moves to a short `/tmp` path derived from a hash
271
+ of the intended one. Every entry point derives it the same way, so clients still
272
+ find the daemon.
273
+
274
+ Plain `stata-code run`, `run --daemon`, and the MCP server all drive the same
275
+ engine and return the same `RunResult` schema — they differ only in how long the
276
+ Stata session lives.
277
+
228
278
  ### One-Command Client Setup
229
279
 
230
280
  `stata-code setup` writes the MCP server entry into a client's config — the
@@ -474,11 +524,14 @@ The extension still requires the MCP extra on your system Python (`pip install "
474
524
 
475
525
  ## Token-Economy Defaults
476
526
 
477
- A typical `stata_run` response is about **10x smaller** than servers that dump logs and images directly. Three design choices drive this:
527
+ A typical `stata_run` response is about **10x smaller** than servers that dump logs and images directly. Four design choices drive this:
528
+
529
+ 1. **Logs return `head` + `tail` + `ref`** by default. Full logs are fetched on demand via `get_log(ref)`, or grepped in place with `search_log(ref, pattern)`. A Stata regression log can be about 6,000 tokens; `stata-code` returns about 600 by default.
530
+ 2. **Graphs return refs, not inline base64**. A 30 KB PNG can become about 50,000 base64 tokens; returning a ref avoids that unless the agent actually needs the bytes. When they are requested inline, they come back as real MCP image content blocks — viewable by a vision-capable client, rather than base64 buried in a JSON string that costs tokens and shows nothing.
531
+ 3. **Each estimation is described once.** By default (`include_results: "scalars"`) `r()` / `e()` scalars and macros are inline, while every matrix becomes a `matrix://` stub carrying just its shape. Without this, one regression ships the same numbers four times — `e(b)`, `e(V)`'s label lists, `e(beta)`, `r(table)` — on top of `results.estimation`, which already holds the typed coefficient table. A 123-term regression drops from ~57 KB to ~28 KB. Raw values stay one `get_matrix(ref)` away, and `include_results: "full"` restores the old shape.
532
+ 4. **Errors are typed**. Agents can check `err.kind == "varname_not_found"` instead of regex-parsing English logs.
478
533
 
479
- 1. **Logs return `head` + `tail` + `ref`** by default. Full logs are fetched on demand via `get_log(ref)`. A Stata regression log can be about 6,000 tokens; `stata-code` returns about 600 by default.
480
- 2. **Graphs return refs, not inline base64**. A 30 KB PNG can become about 50,000 base64 tokens; returning a ref avoids that unless the agent actually needs the bytes.
481
- 3. **Errors are typed**. Agents can check `err.kind == "varname_not_found"` instead of regex-parsing English logs.
534
+ Two further knobs matter for fixed-effect-heavy work: `include_estimation: "summary"` keeps the model-level block and drops per-term rows, and `max_coefficients` caps the table. Either way `estimation.n_coefficients` reports the model's true size and `coefficients_truncated` flags the cut, so a trimmed table is never mistaken for a smaller model.
482
535
 
483
536
  For example, a misspelled variable returns a structured error:
484
537
 
@@ -490,6 +543,7 @@ For example, a misspelled variable returns a structured error:
490
543
  "kind": "varname_not_found",
491
544
  "varname": "mpgg",
492
545
  "line": 3,
546
+ "source_file": null,
493
547
  "context": {
494
548
  "before": ["use auto"],
495
549
  "failing": "summarize mpgg",
@@ -497,11 +551,19 @@ For example, a misspelled variable returns a structured error:
497
551
  },
498
552
  "suggestions": [
499
553
  {"action": "Did you mean `mpg`?", "command": "describe"}
500
- ]
554
+ ],
555
+ "recovery": {
556
+ "category": "user_code",
557
+ "retriable": false,
558
+ "needs_code_change": true,
559
+ "needs_user_input": false
560
+ }
501
561
  }
502
562
  }
503
563
  ```
504
564
 
565
+ When the failing command lives inside a script you invoked (`do "analysis.do"`), `line` and `context` point *inside that script* and `source_file` names it — so an agent does not have to re-read the file to find the offending line.
566
+
505
567
  The full schema is in [SCHEMA.md](SCHEMA.md).
506
568
 
507
569
  ---
@@ -537,7 +599,7 @@ stata_code/
537
599
  | Standalone MCP | ✓ | ✓ | bundled with VS Code | — (bespoke HTTP + copy-paste) | — |
538
600
  | Jupyter kernel | ✓ | — | — | — | ✓ |
539
601
  | Unified result schema | ✓ ([SCHEMA.md](SCHEMA.md)) | per-tool | per-tool | raw log to the agent | per-tool |
540
- | Typed errors + suggestions | ✓ (32 kinds) | — | — | — | — |
602
+ | Typed errors + suggestions | ✓ (34 kinds) | — | — | — | — |
541
603
  | Token-economy defaults | ✓ (log refs, graph refs) | — | — | — | — |
542
604
  | Command-safety guard | ✓ (`shell`/`erase`/`rmdir`/`!`) | ✓ (27-rule) | — | — | — |
543
605
  | Bash / plain-terminal CLI | ✓ (`stata-code run`) | ✓ | — | — | — |
@@ -551,7 +613,7 @@ stata_code/
551
613
 
552
614
  `stata-code` owns the **agent-native, typed-contract** lane: one structured
553
615
  `RunResult` schema across MCP, Jupyter, VS Code, and a plain-terminal CLI; a
554
- 32-kind error taxonomy with recovery contracts; token-economy refs; and now a
616
+ 34-kind error taxonomy with recovery contracts; token-economy refs; and now a
555
617
  console backend (Stata 13+) plus a zero-Python binary. Editor-first tools like
556
618
  `stata-all-in-one` lead on human IDE polish and hand the agent raw log text;
557
619
  `stata-code` matches their onboarding/version reach while keeping the typed
@@ -559,6 +621,49 @@ execution contract they don't have. See
559
621
  [docs/competitive-landscape.md](docs/competitive-landscape.md) for the full
560
622
  teardown.
561
623
 
624
+ ### Versus calling the Stata CLI directly
625
+
626
+ To be clear up front: **you can drive Stata from a shell with no tooling at all.**
627
+ Most Stata installs ship a command-line executable, and for one-shot execution it
628
+ is perfectly good — no reason to add a dependency:
629
+
630
+ ```bash
631
+ /Applications/Stata/StataMP.app/Contents/MacOS/stata-mp -b do analysis.do
632
+ printf 'sysuse auto, clear\nsummarize mpg\nexit, clear\n' | stata-mp -q
633
+ ```
634
+
635
+ What's worth knowing is where that path breaks down. Both columns below drive the
636
+ **same Stata binary and parse the same log** (`stata-code` via `--backend console`);
637
+ the only difference is the wrapper around it:
638
+
639
+ | | plain `stata-mp -b` | `stata-code run` |
640
+ | --- | --- | --- |
641
+ | Exit code on error | **`0`** — fails silently | `1` |
642
+ | How you learn it failed | grep the log for `r(111);` yourself | `ok=False`, `rc=111` |
643
+ | Error classification | none | `[varname_not_found]` (34 kinds) |
644
+ | Recovery hint | none | "Run `describe` to list available variables" |
645
+ | Getting a coefficient | scrape the aligned ASCII table | `e.scalars["r2"] → 0.6515312529087511` (float) |
646
+ | State across calls | no | `run --daemon` (see below) |
647
+
648
+ The first row is the one that bites: Stata's batch mode **returns exit code 0 even
649
+ when the code errors out**. In an automated loop, an agent sees `exit 0`, moves on,
650
+ and treats a failed run's output as valid.
651
+
652
+ Rule of thumb:
653
+
654
+ - **One-shot execution** ("run this do-file and show me the output") — the plain
655
+ CLI is fine; don't over-engineer it.
656
+ - **Scripts / CI** (anything that branches on success) — use `stata-code run` and
657
+ get a trustworthy signal for near-zero cost.
658
+ - **Iterative analysis** (many rounds, reading intermediate results, deciding the
659
+ next step from `e()`) — use `run --daemon` or MCP; otherwise statelessness and
660
+ text-scraping compound round over round.
661
+
662
+ > Without `--daemon`, `stata-code run` **starts a fresh process per invocation**:
663
+ > multiple `-e` snippets in one call share a session, but in-memory data is gone
664
+ > once the process exits. With `--daemon` the data stays in a resident process —
665
+ > see [the session daemon](#the-session-daemon-state-across-invocations).
666
+
562
667
  ---
563
668
 
564
669
  ## Roadmap
@@ -568,11 +673,12 @@ teardown.
568
673
  - v1.0 result schema ([SCHEMA.md](SCHEMA.md))
569
674
  - `pystata`-based runner with native-typed `r()`, `e()`, and matrices
570
675
  - Multi-session via Stata frames (`session_id` accepts `[A-Za-z0-9_-]+`; ids such as `model-a` are mapped to private legal frame names internally while the public id is echoed back)
676
+ - Resident session daemon (`stata-code daemon`, `run --daemon`) so CLI invocations share one live Stata session over a mode-0600 Unix socket, with idle retirement
571
677
  - Per-line error attribution: line number, context, commands_executed
572
678
  - Graph capture: `png` / `svg` / `pdf` with ref store and source-command attribution
573
679
  - Log truncation with ref store
574
680
  - Warning extraction: 5 categories + generic notes
575
- - 32-kind error taxonomy with canonical suggestions
681
+ - 34-kind error taxonomy with canonical suggestions and a machine-readable `recovery` verdict (retriable / needs-code-change / needs-user-input)
576
682
  - MCP server: 21 tools, including notebook navigation / search / atomic edits, the run-bundle index (`list_runs`), log grep (`search_log`), dataset inspection (`inspect_data`), static linting (`lint_do`), and package installation (`install_package`)
577
683
  - Command-safety guard: OS-escape / file-deletion commands (`shell`, `winexec`, `erase`, `rm`, `rmdir`, `!`) are blocked before Stata runs; configurable via `STATA_CODE_COMMAND_POLICY` / `STATA_CODE_POLICY_ALLOW` / `STATA_CODE_POLICY_BLOCK`
578
684
  - Bash / plain-terminal surface: `stata-code run` (a `.do` file, `-e` snippets, or stdin) prints the same structured `RunResult` any agent that can shell out can consume; `stata-code lint` runs the linter; `stata-code setup` writes MCP client configs
@@ -580,8 +686,12 @@ teardown.
580
686
  - Zero-Python standalone binary ([`scripts/build_standalone.py`](scripts/build_standalone.py) + CI workflow template [`packaging/standalone.github-workflow.yml`](packaging/standalone.github-workflow.yml)); with `--backend console` it is a fully Python-free path to typed results
581
687
  - One-click VS Code onboarding: the extension offers to create a workspace `.venv` and install the server (command palette: “Stata: Set Up MCP Server”)
582
688
  - Jupyter kernel: rewired to the v1.0 pipeline, kernel logos bundled
583
- - Matrix size cap + `get_matrix(ref)` for large matrices (>10k cells)
584
- - Subprocess-backed hard timeout and cancellation for the public Python API and MCP server: `timeout_ms`, `cancel(session_id)`, and MCP `cancel_session`
689
+ - Result-payload budget: `include_results` (matrices become `matrix://` stubs by default), `include_estimation`, and `max_coefficients`, with `get_matrix(ref)` to pull raw values on demand
690
+ - Subprocess-backed hard timeout and cancellation for the public Python API and MCP server: `timeout_ms` (now an advertised `stata_run` argument, and it budgets queueing too — contention returns `rc=-5` / `session_busy` instead of blocking), `cancel(session_id)`, and MCP `cancel_session`
691
+ - Background execution for multi-minute jobs: `run_in_background` returns a job id; `stata_run_status` (with a bounded `wait_ms`) and `list_background_runs` poll it
692
+ - Error localization inside `do`/`run` scripts: `error.line` + `error.context` resolve within the invoked file and `error.source_file` names it; a failed run still carries a full searchable log
693
+ - Log-handle hygiene: handles leaked by a failed run are closed automatically (`auto_close_logs`), so an aborted script cannot make every later run in that session fail with r(604)
694
+ - Generated-file reporting: `result.outputs` lists the tables, exports and datasets each run wrote, independent of the run-bundle options
585
695
  - Per-cell repair loop on `.ipynb` via `notebook_outline` / `notebook_get_cell` / `notebook_edit_cell` with optimistic-concurrency `expected_source` guards and `origin_cell_id` echo on `RunResult`
586
696
  - Persistent run bundles + `list_runs` query over `manifest.json` files (filter by cell / origin / session / since / ok; page with limit / offset)
587
697
  - Read-only `stata-code doctor` / `verify` diagnostics for package version,
@@ -596,7 +706,7 @@ teardown.
596
706
 
597
707
  ### Next Up
598
708
 
599
- - Streaming / progress for long runs (`log.complete:false`, incremental log lines) so 20-minute `boottest` / `csdid` jobs report before they finish
709
+ - Streaming / incremental progress for long runs (`log.complete:false`, partial log lines). v0.11's `run_in_background` already unblocks the caller for 20-minute `boottest` / `csdid` jobs, but a running job still reports nothing until it finishes
600
710
  - Hard timeout / cancellation for the Jupyter kernel (move it from the direct in-process runner to the subprocess pool, or an equivalent)
601
711
  - Console backend: graph capture and richer matrix coverage (values currently materialized for the estimation matrices; state is per-call)
602
712
  - Human IDE polish to match editor-first tools: inline graph rendering + DPI export, a scalable data viewer, and an optional "attach to a running Stata" backend