Kotoba

Where to start

What Kotoba isWho she is and where she runs, in one page
InstallingOne package, two commands
First runA key, a model, her first words
The approval gateHow she asks before she acts
The two voice modesLocal voice, or the agent tunnel
The soul fileChange who she is
RoadmapWhat grows next, and what was cut on purpose

Or a section

↑↓ move openesc close124 pages
All pages

Contributing and reference7 of 12

Continuous integration

About 4 minutes to read

On this page

None of this has ever run. The workflows below are files in .github/workflows/; the tree they live in has no git history and has never been pushed anywhere, so no job has ever been scheduled, no runner has ever picked one up, and no step has been observed passing or failing. This page describes what they are written to do. Read it as intent, not as evidence.

Where a step matches a command that was run locally, that is said explicitly.

ci.yml — three jobs

Triggered on a push to main and on every pull request. Permissions are contents: read and nothing else. Runs of the same ref cancel each other.

python

Three legs: Ubuntu on Python 3.11, Ubuntu on 3.13, and Windows on 3.13. fail-fast is off, so one red leg does not hide the others. Thirty-minute timeout, chosen because a test that wedges on Windows would otherwise hold a runner for the six-hour default before anybody learned anything.

Windows is in the matrix because a single unguarded POSIX import once stopped every command there. Only one Windows version: the Python floor is already enforced on Ubuntu.

pip install -e "./api[dev]"
pytest api/tests -q

KOTOBA_CHROMIUM_NO_SANDBOX=1 is set for the whole job. The Linux image ships Chrome and forbids unprivileged user namespaces, so a headless render finds no usable sandbox and hands back nothing — which is a failing assertion rather than a skip. The variable is the product's own answer for a host of that shape.

frontend

npm ci
npx tsc --noEmit --noUnusedLocals --noUnusedParameters
npm test

Node comes from .nvmrc. Both of these commands were run locally while writing these pages: the typecheck exited 0 with no output, and npm test reported 325 passing.

package — the one that matters

This is the job that answers a question no local run can: does pip install give a stranger a working app on a machine with no Node?

It builds the frontend, installs the package, runs the packaged-UI test with KOTOBA_REQUIRE_WEB=1 — which turns "nobody built the web UI" from a skip into a failure — and builds a wheel. Then:

python -m venv /tmp/fresh
/tmp/fresh/bin/pip install "$(ls dist/*.whl)[server,cli]"
# rebuild PATH without any directory that holds node, npm or npx
PATH="$CLEAN" /tmp/fresh/bin/python -c "…"

Inside that interpreter it asserts shutil.which("node") is None, imports kotoba.core.frontend, and requires describe()["kind"] == "packaged".

Two details worth copying:

  • It checks that Node was there to begin with (command -v node || exit 1), because a job that removes something absent proves nothing.
  • It strips whole directories, not the node binary, because the runner image ships its own Node beside the one the setup step installs. Python goes with them, which is why the fresh interpreter is invoked by absolute path.

If you touch packaging, expect this job to have an opinion.

codeql.yml

Static analysis over three languages — python, javascript-typescript and actions — with the security-and-quality query pack. It runs on pushes to main, on pull requests targeting it, and weekly on a Monday, because findings arrive as the rules improve and not only as the code changes. Results land in the repository's Security tab.

The comment at the top says why it exists: this project runs shell commands and Python on the machine of whoever installs it, and a scanner that traces a path from an argument into a subprocess is the one kind of review a solo maintainer cannot do by eye on every change.

Every action here is pinned by commit SHA rather than by tag, because this job holds security-events: write.

release.yml — never walked

Triggered by a tag matching v*, or manually.

The build job runs npm ci, python scripts/build_web.py, then a check that the tag and kotoba.__version__ agree, then the whole test suite, then the packaged-UI test, then python -m build. A release is the one moment a green suite is not optional: the version number it spends cannot be spent again.

The publish job uploads to PyPI using Trusted Publishing — PyPI trusts the workflow by name, so there is no API token to leak or rotate. That arrangement has to be configured once on the PyPI project before the first release will go through, and there is nothing in this repository that can tell you whether it has been. Treat the publication path as unproven.

The attach job uploads the same artifacts to the GitHub release, using the GitHub CLI rather than a third-party action, because that job can write to the repository and that is not a permission to hand to code nobody here reviews.

No package has been published from this tree, and no tag has been pushed from it. PyPI is the only distribution channel the repository contains: searching it for winget, homebrew and chocolatey returns nothing, and no workflow pushes a container image. There are two Dockerfiles and a docker-compose.yml, but they are for running Kotoba yourself, not for publishing an image.

dependabot.yml

Monthly and grouped, across four ecosystems: pip (/api), npm (/), GitHub Actions (/) and Docker (/). Minor and patch updates are grouped into one pull request each. pip and npm are capped at five open pull requests; the other two declare no cap. The reasoning written into the file is that a solo maintainer drowning in pull requests reviews none of them — except security ones, which arrive on their own regardless of the schedule.

The Dockerfiles are in there because they are a documented way to run Kotoba, so their base images age in public.

Reproducing CI locally

Everything except the package job:

bash
pytest api/tests -q
npx tsc --noEmit --noUnusedLocals --noUnusedParameters
npm test
python scripts/build_web.py

The package job cannot be reproduced honestly on a machine that has Node installed, which is the point of it.