[!IMPORTANT] Discord is the primary place to ask questions. Join at discord.observal.io.GitHub issues and PRs are for concrete, actionable items. Exploratory discussion belongs on Discord first.
- #contributing, setup help, workflow questions, anything about the contribution process
- #bug, discuss bugs before filing a GitHub issue
- #feature-requests, pitch ideas and get early feedback before writing code
Parts of this guide structure were inspired by the AnkiDroid Development Guide. Attribution given with thanks, check them out if you want another welcoming OSS project to contribute to.
Table of Contents
- Community Standards
- Prerequisites
- Git Setup
- First-time Setup
- Make Targets Reference
- Architecture Overview
- Git Workflow
- Working on the Backend
- Working on the Frontend
- Working on the CLI
- Harness Recommendations
- Pre-commit Hooks
- Pull Request Checklist
- Getting Help
Community Standards
We are a small, active community and we take the quality of interactions seriously.[!WARNING] The following will result in a moderator warning. A second violation results in a temporary or permanent ban from the repository and Discord:Maintainers volunteer their time. Treat them accordingly.
- Pinging contributors, maintainers, or reviewers unnecessarily (outside of a direct reply on your own open PR or issue)
- Submitting low-effort or unreviewed PRs (slop), including unreviewed AI output or autonomous agent submissions
- Violating the Code of Conduct in any channel
- Harassing reviewers over merge timelines
A note on autonomous coding agents
Autonomous coding agents (Devin, SWE-agent, OpenHands, etc.) are not permitted to submit PRs. This is a legal constraint, not a quality judgment. The US Copyright Office’s 2025 guidance confirms that purely AI-generated code has no copyright owner, which breaks our CLA and our ability to license and enforce the project. See the AI Policy for the full explanation.Prerequisites
Verify your Docker installation supports Compose v2:
docker-compose: command not found or a v1 version, upgrade Docker Desktop or install the Compose plugin separately.
Git Setup
SSH authentication
Using SSH is strongly recommended, it avoids password prompts and is required if you enable 2FA on GitHub.Git identity
Set your identity so it matches your GitHub account:Fork and clone
You only need to do this once. For every subsequent contribution, just create a new branch on your existing fork.- Fork the repository on GitHub (top-right Fork button).
- Clone your fork using SSH:
- Add the upstream remote so you can pull in changes from the main repo:
- Verify your remotes:
First-time Setup
Install pre-commit hooks
Do this before your first commit. The hooks run ruff, format checkers, secret scanners, SPDX header injection, and migration chain validation automatically.Start the Docker stack
make rebuild-fast is the same target under another name. It is not a separate dev profile and does not enable hot reload. It builds only the two app images that contain local code:
observal-api, shared by the API, init, and worker servicesobserval-web, used by the web service
Install the CLI
Log in
super@demo.example / super-changeme for the super_admin account.
Demo accounts
Seeded automatically on first startup:Make Targets Reference
Which rebuild target should I use?
make down stops containers started by make rebuild.
Architecture Overview
Observal is a monorepo:- PostgreSQL, relational data (users, agents, registry, feedback)
- ClickHouse, session events, aggregates, audit events, and security events
Git Workflow
Making a new branch
Always branch from the latestmain. Never commit directly to main.
Keeping your branch up to date
Ifmain has moved forward while you were working, rebase your branch onto it:
Dealing with merge conflicts
Conflicts arise when the same file was changed in bothmain and your branch. During git rebase main, git will pause and show you:
HEAD, seven equals signs, and seven greater-than signs followed by the other branch name. The text between them contains the competing changes.
Edit the file to the correct final state (keeping whichever changes are right, or combining them), then:
--force-with-lease rather than --force, it’s safer:
Submitting a pull request
-
Make sure your branch is rebased on the latest
main(see above). -
Push to your fork:
- GitHub will show a banner on your fork offering to open a PR. Click it, or go to the Observal repository directly.
- Fill in the PR template completely. PRs that do not follow the template will be closed.
-
Link the related issue if one exists (
Fixes #123in the PR body closes it automatically on merge).
After review feedback
If a reviewer requests changes, make them on the same branch and push again. Do not open a new PR. If your changes are small fixes to an existing commit, amend rather than adding new commits:Working on the Backend
Running tests
Tests mock all external services. Docker does not need to be running.Testing conventions
New Python tests should follow the Testing Guide. The current suite has mixed historical patterns, so do not rewrite old tests only for style. When adding or touching tests, prefer the clean pattern documented there: one behavior area per file, small local helper factories, hermetic CLI and API test setup, explicit async mocks, and behavior-focused assertions.Running a single test
Code coverage
Coverage is collected automatically on the3.13 matrix run in CI. To generate it locally:
Adding a database migration
observal-server/alembic/versions/. Then verify the chain is intact:
[!CAUTION] Never edit an existing migration file. Always create a new one. A broken migration chain blocks CI and prevents the server from starting.Apply migrations to your local stack:
Connecting to PostgreSQL directly
Connecting to ClickHouse directly
ClickHouse exposes an HTTP interface. Use the Play UI in your browser:Debugging the API
The API server runs with--reload in development mode. Log output is available via:
breakpoint() in Python code, the debugger will pause in the container’s stdout. Or attach a remote debugger by adding debugpy to your dev dependencies and exposing a debug port.
The OpenAPI docs are available at (API port, not through the LB which blocks these paths):
http://localhost:8000/docs(Swagger UI, requires direct API port access)http://localhost:8000/redoc(ReDoc, requires direct API port access)
Note: The nginx LB blocks/docs,/redoc, and/openapi.jsonin production. For local dev, expose the API port directly or usedocker compose exec.
Working on the Frontend
Running the frontend in isolation
/api calls to the backend at http://localhost:8000, configured in web/vite.config.ts. The Docker stack does not publish port 8000, so run the API locally or point the proxy target at http://localhost (the nginx LB).
Design system
All themes are defined in
web/src/app/globals.css as CSS custom properties. Semantic tokens (background, foreground, card, border, primary, secondary, accent, destructive, success, warning, info) map to OKLCH values per theme.
When adding new UI, use the semantic tokens, never hardcode colors. Check all five themes look correct before submitting.
Adding a new API endpoint to the frontend
- Add the TypeScript response type to
web/src/lib/types.ts. - Add the fetch call to
web/src/lib/api.ts. - Add the TanStack Query hook to
web/src/hooks/use-api.ts. - Use the hook in your component.
Screenshots for UI changes
[!IMPORTANT] Any PR that touches the web frontend must include screenshots of all affected screens in the PR description. This is required regardless of how small the change is. Attach screenshots directly to the PR body, not as review comments.
Working on the CLI
Reinstalling after changes
The CLI is installed as a uv tool. Changes to source files are reflected immediately because it is installed in editable mode (--editable). If you add new entry points or change pyproject.toml, reinstall:
Testing session delivery
Run a harness session with hooks installed, then reconcile and inspect exporter status:Harness Recommendations
VS Code
Install these extensions for the best experience:- Python (
ms-python.python), LSP, type checking - Pylance (
ms-python.vscode-pylance), fast type inference - Ruff (
charliermarsh.ruff), linting and formatting on save - ESLint (
dbaeumer.vscode-eslint), JavaScript/TypeScript linting - Prettier (
esbenp.prettier-vscode), TypeScript formatting - Docker (
ms-azuretools.vscode-docker), container management
settings.json additions:
PyCharm / IntelliJ
- Open the project root as a PyCharm project.
- Set the Python interpreter to the uv-managed venv:
observal-server/.venv/bin/python. - Mark
observal-serveras the sources root. - Install the Ruff plugin for in-editor linting.
- Enable File Watchers to run
ruff formaton save.
Pre-commit Hooks
Install once withmake hooks. The hooks run automatically on every git commit:
If a hook fails, fix the reported issue and commit again. To bypass in an emergency (not recommended):
Pull Request Checklist
Before opening a PR:-
make testpasses locally -
make lintpasses with no errors - Branch is rebased on the latest
main - Commits follow Conventional Commits format
- All commits are signed off with SPDX headers (pre-commit hook handles this automatically)
- PR template is filled in completely and reviewed by the accountable human contributor
- CHANGELOG.md updated under
[Unreleased]for user-facing changes - Frontend changes include screenshots in the PR body
- AI-assisted contributions labelled with tool name and version (see AI Policy)
- No unattended agent submissions without meaningful human direction and review (see AI Policy)
Getting Help
If you are stuck, the best place to ask is #contributing on Discord. Search the channel history first, most common questions have been answered there already. For bugs use #bug. For feature ideas use #feature-requests.[!NOTE] Do not open a GitHub issue just to ask a question. Issues are for confirmed bugs and accepted feature requests. Questions belong on Discord.

