Releasing `ainl`
This document describes how to cut a PyPI-ready release of the ainl package defined in pyproject.toml(../pyproject.toml).
Releasing ainl
This document describes how to cut a PyPI-ready release of the ainl package defined in pyproject.toml.
Latest version in this tree: 1.8.0 (see pyproject.toml, runtime/engine.py RUNTIME_VERSION, CITATION.cff). Older versions remain documented in docs/CHANGELOG.md and docs/RELEASE_NOTES.md.
PyPI lag: Marketing and operator docs may name v1.8.0 as the current line while pyproject.toml and RUNTIME_VERSION match that tag. Until the wheel/sdist is uploaded, pip index versions ainativelang (or the PyPI project page) still shows the prior release. After publish, confirm 1.8.0 appears on PyPI before telling users to pip install -U ainativelang for this line.
Headline repo statistics: Before tagging, run python scripts/refresh_repo_stats.py so STATUS.yaml and the Repository Layout block in AGENTS.md match current line counts, test inventory, and pytest --collect-only output. See CONTRIBUTING.md (optional pre-commit install auto-runs the refresh on relevant commits). The repo-stats GitHub Actions workflow runs --check on every pull request and push to main.
v1.8.0 — publish checklist (this tree)
- Version surfaces aligned:
pyproject.tomlversion,runtime/engine.pyRUNTIME_VERSION,CITATION.cffversion+date-released,tooling/bot_bootstrap.json,tests/emits/server/runtime/engine.pyRUNTIME_VERSION,aiNativeLang.ymlversion(if present). - Lockfile:
uv lock(editableainativelangstanza inuv.lockmatchespyproject.toml). - Notes:
docs/CHANGELOG.md§ v1.8.0,docs/RELEASE_NOTES.md§ AINL v1.8.0. - CI / local gates: green
main(especiallyparser-compat/ wishlist); locally runmake conformance(or project equivalent) andpython -m pyteston Python 3.10 minimum; optionalRelease Gatesworkflow on the release commit. - Pre-tag smoke (MCP + HTTP payments): see Pre-flight block below (wishlist strict + two
ainl runlines). - Build:
rm -rf dist/ && uv build && uvx twine check dist/(orpython -m build+twine check). - Upload:
uv publish(token/OIDC per above) or publish a GitHub Release for tagv1.8.0so.github/workflows/publish-pypi.ymlruns. - Tag (if not via GitHub Release):
git tag -a v1.8.0 -m "ainl 1.8.0"thengit push origin v1.8.0— tag must matchpyproject.toml.
Manual smoke: A2A adapter (before tagging)
Run once against a real Armara (or other) A2A endpoint, or a local mock (see tests/test_a2a_adapter_integration.py for a pattern):
- Minimal allowlist:
ainl run examples/compact/a2a_delegate.ainl --json --enable-adapter a2a --a2a-allow-hosts 'YOUR_HOST'(plus frame/config your program needs; seedocs/integrations/A2A_ADAPTER.md). - Strict + local (as documented there): repeat with
--a2a-strict-ssrf, and only if intended--a2a-allow-insecure-localfor loopback — confirm behavior matches the threat model inA2A_ADAPTER.
Public API surface (for downstream apps)
Downstream code should depend on stable behavior, not private scripts:
| Area | Intended use |
|------|----------------|
| Compiler | compiler_v2.AICodeCompiler — compile .ainl / fragments to IR. |
| Runtime | runtime.engine.RuntimeEngine — execute IR. |
| Hybrid wrappers | runtime.wrappers.langgraph_wrapper.run_ainl_graph, runtime.wrappers.temporal_wrapper.execute_ainl_activity. |
| Emitters (optional) | scripts.emit_langgraph, scripts.emit_temporal when used as libraries from a checkout. |
CLI entry points are listed under [project.scripts] in pyproject.toml (ainl, ainl-validate, …).
Generated file templates (LangGraph / Temporal output) may evolve between minors; consumers should re-emit from source when upgrading.
Version bump
- Update
versioninpyproject.toml(semantic versioning). - Update
docs/CHANGELOG.md(or root changelog policy) with user-facing notes. - Ensure
make conformanceandpytestpass on Python 3.10 (minimum supported). - Optional: run
make benchmark PYTHON=./.venv-py310/bin/pythonand commit refreshedBENCHMARK.md/tooling/benchmark_size.json/tooling/benchmark_runtime_results.jsonif you track full baselines onmain. - For CI regression parity, run
make benchmark-ci PYTHON=./.venv-py310/bin/pythonand committooling/benchmark_size_ci.jsonandtooling/benchmark_runtime_ci.jsonwhen they drift. GitHub Actionsbenchmark-regressionprefers those*_ci.jsonfiles on the baseline commit when present (seeBENCHMARK.md§ CI regression baselines). - Confirm install hardening gates are green (including non-root container smoke +
constraints/py313-mcp.txthealth workflow status).
Build and upload
Preferred (uses project uv.lock):
rm -rf dist/
uv build
uvx twine check dist/*
Upload to PyPI (create an API token on pypi.org with scope for ainativelang):
UV_PUBLISH_TOKEN=pypi-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx uv publish
uv publish reads UV_PUBLISH_TOKEN (or --token). Do not commit tokens. A local .env may define PYPI_API_KEY — export it as UV_PUBLISH_TOKEN for the same value.
CI: See .github/workflows/publish-pypi.yml.
- Trusted publishing (OIDC): Register this repo + workflow on the PyPI project (Publishing → GitHub). Then use Actions → Publish PyPI → Run workflow, or publish a GitHub Release (
release: published). - API token: Add a repository secret
PYPI_API_TOKEN(PyPI API token scoped toainativelang). When this secret is set, the workflow uploads with__token__+ that password; when unset, the action uses OIDC only (same as before).
Classic tools (equivalent):
python -m pip install --upgrade build twine
python -m build
twine check dist/*
twine upload dist/*
Use TestPyPI first if desired: twine upload --repository testpypi dist/* or uv publish --publish-url https://test.pypi.org/legacy/ --token ....
Required release gates (automation)
Before tagging/uploading, ensure Release Gates GitHub workflow passes:
- wheel build +
twine check - wheel install smoke (
import runtime.compat, adapters, cli.main) ainl --helpandainl-mcp --helppython -m pip checkainl install-mcp --host openclaw --dry-runainl install-mcp --host zeroclaw --dry-run- install sections in release notes explicitly mention install-regression status (pass/fail and any known host-lane caveats)
For Python 3.13 sandbox compatibility, use:
python -m pip install --constraint constraints/py313-mcp.txt "ainativelang[mcp]"
Git tag
After upload:
git tag -a vX.Y.Z -m "ainl X.Y.Z"
git push origin vX.Y.Z
Tags should match pyproject.toml version (with or without v prefix — pick one convention and stick to it).
Parity before you tag (1.7.x+): after any pyproject.toml version change, run uv lock so uv.lock lists the editable ainativelang at the same version. Keep CITATION.cff version and date-released aligned with the release you are cutting, together with RUNTIME_VERSION, tooling/bot_bootstrap.json, and any OpenClaw lock files (e.g. aiNativeLang.yml) that record a project version field.
GitHub Release → PyPI (OIDC)
The workflow .github/workflows/publish-pypi.yml runs on release: published. After main is green (especially parser-compat — wishlist strict + smoke), create a GitHub Release for tag vX.Y.Z and publish it; trusted publishing uploads the wheel/sdist to PyPI. Alternatively use Actions → Publish PyPI → Run workflow once the tag exists.
Pre-flight (local parity with CI wishlist gate):
python -m pytest -q tests/test_wishlist_examples_strict.py
python -m cli.main run examples/wishlist/01_cache_and_memory.ainl --json \
--frame-json '{"session_key":"local","note":"ok"}' 2>/dev/null
python -m cli.main run examples/wishlist/05b_unified_llm_offline_config.ainl --json \
--config examples/wishlist/fixtures/llm_offline.yaml \
--frame-json '{"user_query":"local smoke"}' 2>/dev/null
(2>/dev/null drops the optional sandbox shim line so --json stdout is clean when piping.)
Optional extras (document for users)
[dev]— tests, linters.[benchmark]— size/runtime benches + handwritten baselines +temporalio.[interop]—langgraph,temporalio,aiohttpfor hybrid local runs.