Project-specific guidance for Claude Code sessions in this repo. General publishing docs live in PUBLISHING.md; this file captures what isn't obvious from reading the workflows.
Please think and talk with me in English.
A unified release ships four artifacts under the same version number:
| Channel | Tag | Workflow |
|---|---|---|
TestPyPI (stata-code) |
vX.Y.Z |
publish-testpypi job in .github/workflows/release.yml |
PyPI (stata-code) |
vX.Y.Z |
publish-pypi job in .github/workflows/release.yml |
VS Code Marketplace (stata-code-vscode) |
vscode-vX.Y.Z |
.github/workflows/vscode-release.yml |
| GitHub Release | vX.Y.Z |
tail end of release.yml |
Eight version literals (across six files) must move together, or the
release.yml "Verify tag matches project versions" gate (scripts/check_versions.py)
fails the build before any publish runs. Bump all of them, or run the guard
locally first (python3 scripts/check_versions.py --tag vX.Y.Z):
pyproject.toml→[project] versionstata_code/__init__.py→__version__stata_code/mcp/server.py→__version__vscode/package.json→versionvscode/package-lock.json→ top-levelversionandpackages[""].version(two sites).claude-plugin/plugin.json→version.claude-plugin/marketplace.json→metadata.versionand eachplugins[*].version(two sites)
vscode/src/mcpClient.ts imports the extension version from
vscode/package.json for the MCP handshake, so it should not carry a separate
literal.
Both PyPI and TestPyPI use OIDC (no API tokens in repo secrets). They are separate sites with separate publisher configs — each must be set up independently:
| Site | Manage URL | Environment |
|---|---|---|
| PyPI | https://pypi.org/manage/project/stata-code/settings/publishing/ | pypi |
| TestPyPI | https://test.pypi.org/manage/project/stata-code/settings/publishing/ | testpypi |
For both, the publisher values are:
- Owner:
brycewang-stanford - Repository:
stata-code(hyphen — not the local dir namestata_code) - Workflow:
release.yml - Environment:
pypiortestpypi(must match the job'senvironment.name)
Configuring trusted publishers on another project (e.g. statspai) does not
carry over — each (site, project) pair has its own publisher list.
The release.yml flow is build → publish-testpypi → publish-pypi → github-release.
Both publish jobs are continue-on-error: true, so a missing TestPyPI publisher
does not block the PyPI publish or the GitHub Release.
Symptom: release.yml runs, publish-pypi fails with
invalid-publisher: valid token, but no corresponding publisher. Because
publish-pypi has continue-on-error: true, the overall run reports success and
the GitHub Release still gets created — but PyPI has nothing.
Fix without re-tagging:
-
Configure / correct the trusted publisher on PyPI (values above).
-
Re-run only the failed job — the sdist/wheel artifact is still attached to the original run, so no rebuild is needed:
gh run rerun <run-id> --failed gh run watch <run-id> --exit-status
-
Verify (next section).
Do not delete and re-push vX.Y.Z to retry. The artifact is already on the
run, the GitHub Release exists, and PyPI rejects re-uploads of the same version
anyway.
The convenience endpoint https://pypi.org/pypi/stata-code/json is heavily
CDN-cached and can lag the actual publish by minutes. To get an authoritative
answer immediately:
# Per-version JSON (200 = published, 404 = not yet)
curl -s -o /dev/null -w "%{http_code}\n" https://pypi.org/pypi/stata-code/X.Y.Z/json
# Simple index (always fresh)
curl -s -H "Accept: application/vnd.pypi.simple.v1+json" \
https://pypi.org/simple/stata-code/ | python3 -c \
"import json,sys; print(json.load(sys.stdin).get('versions', []))"For a sideload install, build the .vsix locally (the repo does not track
built artifacts):
cd vscode && npm ci && npm run package:vsix
code --install-extension stata-code-vscode-X.Y.Z.vsix --forceThe Marketplace publish runs on the vscode-vX.Y.Z tag, independent of the PyPI
release.
The extension auto-discovers a Python interpreter for the MCP server in this order (see vscode/src/mcpClient.ts and vscode/src/extension.ts):
- Per-workspace
.venv/venvin any workspace folder - The Python configured in
stataCode.pythonPath(settings) - System
python3
The MCP client is reset whenever stataCode.* settings change so users don't have
to reload the window.