Contributing and reference10 of 12
Versioning, the changelog, and how a release is meant to work
On this page
Where the version number really comes from
One place:
# api/src/kotoba/__init__.py
__version__ = "0.10.0"
Everything else reads it or is checked against it.
| Where a version appears | How it gets there |
|---|---|
api/pyproject.toml | dynamic = ["version"], resolved by version = { attr = "kotoba.__version__" } |
package.json | written by hand, and pinned by a test to equal kotoba.__version__ |
api/src/kotoba/web/kotoba-build.json | stamped by scripts/build_web.py at build time |
| A git tag | checked against kotoba.__version__ by the release workflow |
api/tests/test_packaged_data.py holds package.json and the package to the same number, because two
version strings for one release with nothing comparing them means the interface can name a version the
wheel has never heard of. api/tests/test_the_wheel_carries_the_web_ui.py refuses a build stamped for a
different version. Both were run: they pass.
To bump the version, change __init__.py and package.json together. Nothing else.
The numbering
Versions start at 0.10.
- A fix or a small addition moves the last number: 0.10.1, 0.10.2.
- A release that changes how Kotoba behaves for you moves the middle one.
- The leading zero stays until the shape of Kotoba stops moving; a 1 will be a promise about that.
- Anything that changes behaviour says so in the changelog in plain words before it says so in a number.
Read the middle number as ten, not as one: 0.1 and 0.10 are different versions and 0.10 is the
later of the two. The third number is not decoration either — Python normalises 0.010 to 0.10, so a
version written with a stray zero publishes as a release that already exists.
The changelog
CHANGELOG.md, newest first, with an ## Unreleased section at the top. Add your entry there in the
same pull request as the change, in the same plain register the rest of the file uses: what it does for
the person using Kotoba, not which function you renamed.
At the time of writing, ## Unreleased says Nothing yet, and ## 0.10.0 — the first release is the
only entry.
What a release is written to do
This has never been done. No tag has been pushed from this tree,
release.ymlhas never been executed, and nothing has been published to PyPI from it. What follows is what the workflow file says, not a procedure anyone has walked.
Pushing a tag matching v* is meant to trigger three jobs:
build — install Node and Python 3.13, npm ci, python scripts/build_web.py, then check that the
tag and kotoba.__version__ agree, then run the entire test suite, then the packaged-UI test with
KOTOBA_REQUIRE_WEB=1, then python -m build --outdir dist ./api. The distributions go up as an
artifact.
A release is the one moment a green suite is not optional: the version number it spends cannot be spent again, and nothing else links that workflow to the one that runs on a push.
publish — uploads to PyPI using Trusted Publishing, so there is no API token stored anywhere. The
job carries id-token: write and runs in a pypi environment. Trusted Publishing has to be
configured once on the PyPI project before the first release can go through, and nothing in this
repository records whether that has been done.
attach — uploads the same distributions to the GitHub release, with the GitHub CLI rather than a
third-party action, because that job can write to the repository.
Both download steps and the publish action are pinned by commit SHA, not by tag: a moving tag is somebody else's decision about what runs with your write permission. The jobs that only read stay on major tags.
Supported versions
SECURITY.md states it in one line: the latest release. Older versions get no backports — the fix
is the upgrade.
What ships in a release
One distribution, kotoba-companion, on PyPI. The import name, the command and the companion are all
kotoba; only the distribution name differs, because kotoba on the index is somebody else's library.
The wheel carries:
- the package under
api/src/kotoba/ - the personality file, its templates and the skills, as package data
- the terminal's face art
- the compiled web UI under
kotoba/web/— which the build backend refuses to omit or to ship stale LICENSEandTHIRD_PARTY_NOTICES.md
Install extras pick the rest:
pip install "kotoba-companion[server,voice,cli,web,mcp]"
There is no other channel. No winget manifest, no Homebrew formula, no published container image.
