XO Space

Contributing to XO Space

Set up a development checkout, follow the architecture contracts, and validate a contribution against development.

Contribute to the server, agent adapters, project services, or the build-free Space UI. Start from development and submit your pull request to development; main is the release branch.

The repository's AGENTS.md, CONTRIBUTING.md, and DEVELOPING.md are the working contracts. Check them in the revision you are changing.

Set up a checkout

Use Linux, macOS, or WSL. Contributor mode needs Python 3.12+ and git:

git clone https://github.com/quirq-ai/xo-space.git
cd xo-space
git checkout development
git switch -c fix/my-change
./cowork-api.sh install
QUIRQ_SKIP_BOOT_INSTALL=1 ./cowork-api.sh dev

Open the printed /space/ URL. Development mode enables reload; the preferred port is 5002, with a fallback when it is busy. Use PORT=5010 if you need an explicit alternative.

To exercise the installation path instead, run ./install.sh from your checkout. It creates a uv-managed environment and leaves your git working tree alone. That venv has no pip; install extra packages with uv pip install --python venv/bin/python <package>. Choose explicit workspace/state roots when your development checkout should not also be the project root.

Know where a change belongs

LocationResponsibility
server.pyApplication wiring, lifespan jobs, router mounts.
routers/HTTP request/response handling.
services/Space-level services, shared storage, connections, Inbox, and swarm client.
services/cowork_agent/Agent execution, registry, adapters, session plumbing, watcher.
config/agents/<name>/Runtime manifests, settings, capabilities, lifecycle setup.
space_ui/HTML, CSS, and browser ES modules, with no bundler.
tests/Hermetic Python unittest suite and browser/module harnesses.
plugin/ and .agents/Agent plugin/skill bundles that must stay synchronized.

New API work uses modular Plane B, selected by AGENT_NAME. The older /ask_question* Plane A, selected by AI_PROVIDER, remains a separate compatibility surface.

Preserve the contracts

  • Keep routers thin and API paths, request bodies, and response shapes compatible.
  • Resolve agent-specific behavior through the capability loader. Agent-specific implementations belong in adapters/<name>/, config/agents/<name>/, or the legacy config/models/<name>/ tree; follow the documented frozen exceptions.
  • Keep conversations and credentials out of project folders. Portable .xo/ records and machine-local .quirq/ telemetry have different owners and lifetimes.
  • Use the owning service/API for writes. In particular, todos have one HTTP write path; the watcher does not mirror every agent's native task tool.
  • Run external commands through utils.commands with argument lists. Route all xo-swarm-api calls through services/swarm_api/.
  • Use async network/subprocess operations and avoid logging secrets or prompt text.
  • Isolate tests with temporary roots and mocked external services; never point a test at a real .quirq/ directory or credential file.

Change the Space UI

Each view has a module under space_ui/js/views/. The registry owns navigation, parents, hash routes, lazy mounting, and view isolation. Views do not import one another: use ctx.switchTo() or the documented shared events. HTTP calls use js/core/api.js.

When changing a view:

  1. Update its module, the matching documentation page, and the in-app Wiki topic summary or link when needed.
  2. Bump its ?v= import stamp in js/app.js, then the app stamp in index.html. Bump changed stylesheet URLs and shared imports where appropriate.
  3. Run node --check on changed JavaScript modules.
  4. Exercise the actual user path in a browser, including deep links and any related lens transitions. Include screenshots in the PR for visible changes.

Add a runtime

Add config/agents/<name>/manifest.json and its settings/capability declarations, then services/cowork_agent/adapters/<name>/adapter.py. Export Adapter as a BaseAgentAdapter implementation with run, stream, and adapter_name.

Optional modules supply models, usage, sessions, streaming, watcher sources, or session telemetry. routes.py supplies agent-owned routes, mounted only when that agent is active. Missing optional capabilities degrade to their documented empty/501 responses; an import failure inside an existing module is a bug and must remain visible.

Use the adapter guide for the full contract. No central hardcoded agent registry should need editing.

Validate and submit

venv/bin/python -m unittest discover -s tests -t .
venv/bin/python scripts/check_route_parity.py

Also run checks appropriate to the files changed:

# Installer changes
bash tests/install_sh_harness.sh

# Plugin or .agents changes
./scripts/check_plugin_sync.sh

Review the modularity invariant and smoke the relevant capabilities under affected runtimes. Route totals can differ by runtime; the parity script checks the shared core plus each runtime's own routes instead of a fixed count.

Target development. Describe the problem and resulting behavior, link the issue, list the validation you actually ran, and explain any changed contract. Keep the branch current with development. Maintainers promote development to main for releases.