Development¶
This page covers how to change the repository: the environment, tests, documentation checks, the embedded asset rules, and the review and merge workflow. For safety rules that apply to a live fleet, see Operations and troubleshooting.
Environment¶
- The control-plane scripts target Python 3.11 or later on macOS and Linux and use only the standard library. Don't add a third-party runtime dependency without explicit approval.
- The documentation build is the only exception.
docs/requirements.txtpins MkDocs and its theme for the site build. Nothing insrc/,tools/, or the Docker images imports them. - Keep secrets,
.env, SSH private keys, approved uploader JSON files, rendered launch directories, and operational state outside the Git checkout when you can, for example under$HOME/oxidex-ops. - When you add or rename an environment variable, document it in
.env.exampleand in the configuration reference.
Run the tests¶
Run focused tests while you iterate, and run the full discovery suite before you commit:
Run Cargo compilation and the full Python suite on an eligible remote Spot worker through the reviewed build route. Local work is limited to edits, Git, formatting, bounded metadata and control checks, and prose lint.
The reviewed remote proof runs the complete signed-source suite inside the admitted builder container. Set
OXIDEX_CONTROL_REPO to a reviewed OxiDex checkout that has the test-infra-ci-remote recipe, then run:
The fixed CI-only profile verifies module coverage, discovered and executed test identities, actual test
results, and artifact hashes. There is no local full-suite fallback for just test. Don't claim that
just test ran if you ran only local unittest discovery. Local tests, remote proof runs, GitHub checks,
merged commits, rendered startup payloads, and live observations each prove something different, so state the
exact command and environment for every verification claim.
Remote proof contracts¶
tools/infra_python_complete_proof.pypins the SHA-256 oftools/infra_python_test_proof.pyinPRODUCER_SHA. After a reviewed change to the producer, updatePRODUCER_SHAand runtests/test_infra_python_test_proof.py. That test checks the producer's behavior, not the pin: onlytools/infra_python_complete_proof.pyitself, run throughjust testorjust infra-python-complete-testsin the admitted builder, detects a stalePRODUCER_SHA.tools/infra_python_test_proof.pyenforces minimum counts for test cases (MIN_CASES, 854) and test modules (MIN_MODULES, 82). The complete proof replaces those floors withmax(774, module count)cases andmax(76, module count)modules. The producer also checks that everytests/test_*.pymodule matches its digest in the manifest, with no duplicate test identities. Don't delete or merge test modules below those floors without a reviewed contract update.
Keep embedded assets in sync¶
Some modules embed scripts that tests compare byte for byte with the files on disk:
| Module | Embeds or reads |
|---|---|
src/lib/runner_assets.py |
runner/clean_target.py, runner/docker-wrapper.py, runner/job-completed.sh, runner/Dockerfile, runner/entrypoint.sh, and cloud-init/startup.sh. Update the constant in the same commit as the file. |
src/lib/builder_assets.py |
The legacy shared-host builder files: builder/Dockerfile, builder/entrypoint.py, and builder/host-launcher.py. |
src/lib/builder_only_assets.py |
Dedicated builder files from builder/standalone/, builder/dedicated/, and cloud-init/builder-startup.sh. Keep the file lists, permissions, and runtime-update target manifests in src/lib/builder_runtime_update.py aligned. |
Write and check documentation¶
Documentation follows the Google developer documentation style guide:
sentence-case headings, active voice, direct instructions, and explicit prerequisites. Preserve command names
and technical meaning when you fix prose. Check command examples against --help output and the source.
Install Vale from its installation guide. Then synchronize the configured Google rules and lint every documentation file:
A clean Vale result covers the configured rules, not every editorial requirement of the style guide.
When you add a Markdown file:
- Register it in
.vale.iniunderBasedOnStyles = Google. - Add it to the
navsection ofmkdocs.yml. The site build runs in strict mode, so an unlisted page, a broken link, or a missing anchor fails the build. - Allowlist any new root file or asset path in
.gitignore. The file starts with*, so Git ignores anything that isn't un-ignored with a!entry.docs/*.mdanddocs/requirements.txtare already allowed.
Build the site locally¶
Create a virtual environment outside the checkout, install the pinned build dependencies, and build in strict mode:
python3 -m venv "$HOME/oxidex-ops/docs-venv"
"$HOME/oxidex-ops/docs-venv/bin/pip" install -r docs/requirements.txt
"$HOME/oxidex-ops/docs-venv/bin/mkdocs" build --strict --site-dir "$HOME/oxidex-ops/docs-site"
mkdocs serve previews the site at http://127.0.0.1:8000. The site/ directory is ignored by Git. The
Documentation workflow (.github/workflows/docs.yml) runs the same strict build on pull requests that
change the documentation. On pushes to main that change the documentation it also deploys the site to GitHub Pages through the
github-pages environment. In the repository settings, set Pages > Source to GitHub Actions once.
The site is public
A GitHub Pages site can be readable by anyone, even when the repository is private. Describing the
architecture, including the edge and tunnel design, is fine. Don't put deployment identifiers in the
documentation: project, account, namespace, runner group, or tunnel IDs, internal hostnames, tokens, or
secret values. Resource names fixed in the code, such as the spot-control-a and spot-control-b slots,
may appear. Use placeholders such as YOUR_PROJECT.
Git and review workflow¶
A task prompt authorizes you to work on an isolated feature branch, commit the task changes, push the branch,
open a pull request against main, resolve review comments, and squash-merge once every review and CI gate
passes. It doesn't authorize force-pushing, changing branch protections, or changing live cloud resources.
Branch, identity, and accounts¶
- Develop on a dedicated branch or an isolated Git worktree based on the latest
origin/main. Never commit or push directly tomain. - Author and SSH-sign commits as
swackhamer(user.name=swackhamer,user.email=swackhamer@users.noreply.github.com,commit.gpgsign=true). Verify the local signature withgit cat-file commit HEAD, and verify GitHub's verification status on the pushed commits. - When several
ghaccounts are logged in, export theswackhamertoken for the shell session so that no command falls back to another stored account:
export GH_TOKEN="$(gh auth token --user swackhamer)"
gh pr status --repo swack-tools/spot-github-runners
- Stage files by explicit path with
git add <path>, so scratch files, local.vale/styles/, and unrelated edits never reach a commit.
Review loop¶
- Run the full unit test suite and Vale on every changed documentation file.
- Get an independent local review of the full diff against
origin/main, and resolve its findings before you open the pull request. - Push the branch and open a pull request that targets
main. Request an independent review on the pull request. - Check each review comment against the code. Fix every valid issue, rerun the tests and Vale, push a signed commit, reply with the fix and the verification evidence, and resolve the thread.
- Request a fresh review on the new head commit. Repeat until the reviewer reports no remaining issues, every thread is resolved, and the required GitHub checks pass.
- Confirm that
mainhasn't advanced in a conflicting way and that the reviewed head commit matches the pull request head. Then squash-merge, pinning the reviewed commit so that the review and CI gate can't be raced: