Development
Toolchain
Python: uv + ruff. Frontend: bun. Warden: cargo.
make setup # uv sync --extra server
make test # every suite CI runs: python × 3 + cargo
make lint # ruff · uv lock --check · clippy · tsc · eslint
The unit suite is designed to run without Docker or Kubernetes — the runners no-op their container calls — so it is fast and CI-safe.
Tests
Each suite discovers its own test_* functions (tests/_runner.py), so writing a test is
enough to make it run. There is no registration list to forget.
uv run python tests/test_unit.py # control plane, ~90 tests
uv run python tests/test_sdk.py # SDK, offline via httpx MockTransport
uv run python tests/test_worker_rewind.py # worker rewind/reconnect robustness
cargo test --manifest-path warden/Cargo.toml
Red-team checks
These need a built sandbox image, so they are not in the unit CI. Run them before a deploy:
make redteam # sandbox isolation boundary
make redteam-pinhole # the firewall opens exactly one endpoint (the Warden pinhole)
make redteam-conceal # no environment tells, combined CA store
Guards worth knowing about
Several tests exist to stop a specific class of drift rather than to check a behaviour. If one fails, it is telling you a duplicate has reappeared:
test_harness_surfaces_are_one_schema— the harness field set across the dataclass, the API body, the agent PATCH model andweb/lib/types.ts. It parses the TypeScript, because the console has no Python to import.test_tool_catalog_is_single_source— the tool/skill catalog is served fromterracore/toolset.py, not re-listed in the console.test_stores_are_owner_only— every durable JSON store is written0600.test_warden_mandatory— no config knob can produce an unmediated sandbox, and the Pod manifest builder has no form that omits the sidecar.
Releases
Releases are automated from Conventional Commits on main:
| Commit prefix | Effect |
|---|---|
fix: |
patch release |
feat: |
minor release |
feat!: / BREAKING CHANGE: footer |
major release |
chore:, docs:, refactor:, test:, ci: |
no release |
semantic-release derives the version, runs scripts/bump-version.sh to stamp it into
pyproject.toml, sdk/pyproject.toml and warden/Cargo.toml, commits, tags, and publishes
the GitHub release. Only then do the three Docker images and the SDK wheel build — so a
published artifact always corresponds to a tag.
One-time setup before the first release
These are console settings, not code. Each one fails a job with a message that does not
obviously name it, so do all three before the first push to main:
- Baseline tag. semantic-release computes the next version from git tags and ignores
the version in
pyproject.toml. With no tags it starts at 1.0.0, not at your manifest version. Tag the first commit before releasing:git tag v0.1.0 <first-commit-sha> && git push origin v0.1.0The release workflow now refuses to run without a
v*tag rather than silently publishing a major. -
PyPI trusted publisher. Create a pending publisher at https://pypi.org/manage/account/publishing/ — project
terrarium-python, ownerOAISP, repositoryterrarium, workflowrelease.yml, environmentpublish and release(this string must match theenvironment:in the workflow’ssdkjob exactly). It must exist before the first upload; otherwise the OIDC exchange 403s and the job fails with a permissions error rather than “you have not configured this”. - GitHub Pages. Settings → Pages → Source: GitHub Actions. Until then
actions/configure-pagesfails and the Docs workflow is red.
Repository secrets
| Secret | Used by |
|---|---|
DOCKERHUB_USERNAME / DOCKERHUB_TOKEN |
pushing the three images |
| (none for PyPI) | the SDK publishes via OIDC trusted publishing |
PyPI trusted publishing needs a one-time configuration on the terrarium-python PyPI
project: owner OAISP, repository terrarium, workflow release.yml, environment pypi.
The distribution is terrarium-python; the import package stays terrarium.
Documentation
This site is Jekyll + Just the Docs, built from docs/ by
GitHub Actions on every push to main.
cd docs && bundle install && bundle exec jekyll serve