Release Process

Use this process before merging release candidates or creating tags.

Preflight

  1. Review openspec/project.md and the affected capability specs.
  2. Confirm the change belongs in the MVP or has an OpenSpec proposal under openspec/changes/.
  3. Update README examples, CHANGELOG.md, and user-facing docs for visible behavior changes.
  4. Regenerate schemas/dataset_spec.schema.json when DatasetSpec changes:
python3 scripts/export_dataset_schema.py

Checks

Run the executable release gate:

scripts/check_release.sh

The script runs linting, strict type checks for the stable package core, compilation, coverage tests, DatasetSpec schema freshness, and the README quickstart smoke flow. The smoke flow verifies the generated manifest reports synthetic: true, source_rows_copied: false, a valid validation report, the expected seed, and expected row counts.

CI and tagged releases also build the wheel and install it in an isolated environment. That smoke check verifies package version metadata, the PEP 561 py.typed marker, console entry points, and test-data-agent doctor --skip-smoke before release attestations are created.

Version And Tag

  1. Bump pyproject.toml and src/test_data_agent/version.py together.
  2. Move relevant CHANGELOG.md entries from Unreleased to the new version.
  3. Commit the release preparation.
  4. Tag the commit after scripts/check_release.sh passes.

Merging a release pull request does not publish a package. Publication starts only after the matching annotated tag is pushed:

git tag -a vX.Y.Z -m "Release vX.Y.Z" <verified-main-commit>
git push origin vX.Y.Z

The tag triggers .github/workflows/release.yml, which creates the GitHub Release and then dispatches the dedicated PyPI Trusted Publishing workflow.

Keep compatibility changes explicit. Breaking DatasetSpec, CLI, artifact, or Python API changes require a migration guide and a versioned changelog entry.

PyPI Trusted Publishing

After creating a GitHub Release, .github/workflows/release.yml explicitly invokes .github/workflows/publish-pypi.yml. The called workflow downloads the release wheel and sdist, validates their embedded distribution name and version, verifies that both were attested by release.yml for the selected tag, and publishes with short-lived GitHub OIDC credentials. The public-index smoke test installs runtime dependencies from the hash-locked uv.lock export and installs the exact PyPI wheel by its verified SHA-256 digest. The workflow does not use a PyPI API token and does not build or execute repository code in the OIDC-enabled publish job.

Configure the pending or project Trusted Publisher with these exact values:

  • PyPI project: agent-paranoid-android
  • GitHub owner: wa-pis
  • GitHub repository: agent-paranoid-android
  • Workflow filename: publish-pypi.yml
  • Environment: pypi

The manual dispatch input exists only for recovering a published GitHub Release that predates the workflow. Duplicate PyPI versions fail loudly; skip-existing is intentionally disabled.

Public Release Readiness

Before making the repository public or announcing a public release, complete the Public Release Checklist. In particular:

  1. Confirm LICENSE, SECURITY.md, CONTRIBUTING.md, issue templates, pull request template, and Dependabot configuration are present.
  2. Confirm author and committer emails are public-safe.
  3. Run a secret scan over the working tree and reachable Git history.
  4. Build and smoke-test the installed wheel:
uv build --no-build-isolation
uv run --isolated --no-project --with ./dist/*.whl \
  python scripts/check_installed_package.py
uv run --isolated --no-project --with ./dist/*.whl \
  test-data-agent doctor --skip-smoke
  1. Enable GitHub security settings after publication: secret scanning, Dependabot alerts, Dependabot security updates, private vulnerability reporting, required CI and dependency-review checks, and active branch/tag rulesets.
  2. Confirm the pypi GitHub environment and matching PyPI Trusted Publisher use the exact workflow identity documented above.