Skip to content

Contributing

This section documents the standard development workflow and quality bar for SilentSwarm changes.

Current topics

  • Environment setup uses Python 3.11+ and editable installs from the repository root.
  • Docstring style for public modules, classes, and functions.
  • Code style expectations: keep changes focused, prefer existing package patterns, and avoid generated artifact churn.
  • Testing expectations: add or update unit tests for new behavior; add integration coverage when behavior crosses leader/fellow/CLI boundaries.
  • Documentation expectations: update the closest README or docs page whenever user-facing behavior, commands, config, or APIs change.

Setup

python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e ".[dev,docs]"
python3 scripts/vendor_frontend.py     # pinned UI libraries into the leader's templates/

Add ,runtime to the extras only on a GPU machine — that pulls PyTorch. The leader is deliberately torch-free and must stay importable without it.

Repository layout

src/silent_swarm/
  leader/        control plane (FastAPI + web UI) - see below
  fellow/        the GPU worker: runner, heartbeat, signals, inventory, log shipper
  runtime/       model execution; runtime/torch/ is the ONLY place torch is imported
  shared/        contracts.py (leader/fellow/CLI wire types) and mesh.py
  transport/     ZeroMQ PUSH/PULL tensor transport
  cli/           the `swarm` CLI, one module per command group
  bootstrap/     the fellow bootstrap entry point

The leader is the largest package, so it is grouped by domain rather than left flat. app.py (the FastAPI app and its lifespan) and route_policy.py (the authorization map covering every route) sit at the top; everything else lives in the subpackage that owns it:

Package Owns
accounts/ users, auth, admin user management, API tokens, the audit log
pools/ the pool store and its HTTP surface
buckets/ pool-scoped training sets and their HTTP surface
cluster/ node lifecycle: bootstrap contract, join sessions, worker registry, signal hub, log buffers
telemetry/ health windows, serving metrics, grades, metric history, VRAM budget, node replacement
notify/ transactional email and operational alerts
training/ jobs, layer partitioning, placement, checkpoints, job metrics
serving/ servable-model export, catalog, inference broker
web/ page builders and templates/ (hand-written HTML/CSS/JS, no framework)

tests/unit/<area>/ mirrors this tree, subpackages included — a new test for leader/telemetry/grades.py belongs in tests/unit/leader/telemetry/. Tests that cut across the whole app (the route-policy sweep, CSRF enforcement, the relay) sit at tests/unit/leader/, next to what they exercise in app.py.

Imports inside the leader are always absolute (from silent_swarm.leader.pools.store import PoolStore); nothing is re-exported from an __init__.py, so every import names the module it comes from.

Validation

Everything CI runs, in the order it runs it:

python -m ruff check .                  # lint
python -m pytest tests/unit -q          # fast suite, no cluster needed
python -m pytest tests/integration -q   # leader/fellow/CLI boundaries
mkdocs build --strict                   # docs must build clean

A new leader API route also has to be classified in leader/route_policy.py — unlisted routes are default-deny, and tests/unit/leader/test_route_policy.py fails until you add them.

Changes that touch the web UI run the frontend job as well:

biome ci .                                   # lints the templates JS
npx typescript@5.7.2 tsc -p jsconfig.json    # // @ts-check + JSDoc, no build step
node scripts/frontend_smoke.mjs              # boots the leader, loads every page

The smoke test fails on any console error, failed request, request to a non-vendored third-party host, or serious/critical axe-core accessibility violation — so a broken page or a regressed contrast ratio fails the build rather than reaching a screenshot.

Documentation screenshots

The User Guide carries a screenshot of every page. Regenerate them whenever the UI changes:

# a throwaway leader with its own state, models root, and cookies over http
SWARM_JOIN_STATE_DB=/tmp/guide.sqlite3 \
SWARM_SERVABLE_MODELS_ROOT=/tmp/guide-models \
SWARM_COOKIE_SECURE=0 \
  python -m uvicorn silent_swarm.leader.app:app --port 8099

python scripts/screenshot_docs.py --base http://127.0.0.1:8099 \
    --state-db /tmp/guide.sqlite3 --models-root /tmp/guide-models \
    --out docs/06_user_guide/img

The script seeds a small but complete cluster — four heartbeating nodes, a deployed two-stage model, an hour of metric history — because a page with no data documents nothing. Needs playwright and a system Chromium.

  • tests/
  • src/
  • docs/
  • exp/

Documentation conventions

Moved here from the documentation home, which is for readers rather than authors:

  • Document current behavior first.
  • Keep navigation task-oriented and shallow.
  • Prefer Mermaid diagrams for system flows and state machines.
  • Keep historical planning separate from operator and developer docs.
  • Keep links relative, and give a link the page's title as its text - a bare some-file.md reads as a directory listing, not as documentation.
  • Verify with python -m mkdocs build --strict.

Building the docs locally

source .venv/bin/activate
pip install -e ".[docs]"
python -m mkdocs serve                 # live reload on http://127.0.0.1:8000
python -m mkdocs build --strict        # what CI runs

A standalone mkdocs serve has no /vendor/ path, so the brand fonts fall back to the system stack - that is expected, and the deployed site serves them from the same origin as the app. See The public web surface.