-
-
Notifications
You must be signed in to change notification settings - Fork 0
Develop, test and deploy
This page covers running Radd locally: the test suite, migrations, and the demo scripts. It also covers how to verify that a UI change renders, and how a change reaches the live instance.
The recommended path runs everything — the API and its runtime dependencies — inside a container, with the server source bind-mounted so edits reload immediately:
podman compose -f compose.dev.yaml up # db + API on :8000— CLAUDE.md, "Running"
The container brings pg_dump/pg_restore, which the app now requires at
startup — not an optional capability. A backup system that silently cannot
back anything up is worse than one that refuses to start:
# pg_dump/pg_restore are runtime requirements, not optional capabilities:
# a backup system that silently cannot back up is worse than one that
# refuses to start (RADD_BACKUP_TOOLS_OPTIONAL downgrades this to a warning).
await backup_postgres.preflight()— server/src/radd/app.py
If you run the server directly on the host, not in the container, your
machine needs a working Postgres client for that preflight check to pass.
Without one, set RADD_BACKUP_TOOLS_OPTIONAL=true. It downgrades the missing
tools from a startup failure to a warning:
podman compose up -d db # Postgres 16 on localhost:5455 (from repo root)
cd server
uv sync
uv run alembic upgrade head
uv run python -m radd.seed --email hussein@hjarrar.com --password radd-dev-1 --name "Hussein Jarrar"
uv run uvicorn --factory radd.app:create_app --host 0.0.0.0 --port 8000— CLAUDE.md, "Running"
Either way, the full app — API and the built web UI together — serves at
http://localhost:8000, with interactive API docs at /docs. Log in with the
credentials you passed to radd.seed.
For active frontend work, run the Vite dev server on port 5173 against the API container:
cd web && npm run dev— CLAUDE.md, "Running"
To refresh the bundle the API serves at :8000 without a dev server running,
or to build without npm on the path at all:
cd web && ./node_modules/.bin/tsc -b && ./node_modules/.bin/vite build— CLAUDE.md, "Frontend conventions"
uv run pytest— CLAUDE.md, "Running"
The suite is self-contained: it re-creates and migrates its own throwaway
radd_test database on a session-scoped fixture and never touches your dev
database.
"""conftest repoints the app at a throwaway `radd_test` database (same Postgres
server; override with RADD_TEST_DATABASE_URL), and a session-scoped fixture
re-creates it and migrates it once per run."""— server/tests/conftest.py
As of 2026-08 the suite runs roughly 1,600 tests. CLAUDE.md rule 4 sets the
bar for what earns a test. A worthwhile test covers a core invariant many
modules depend on — custom field validation, event emission, permission
checks. It is not one unit test per endpoint. server/tests/test_route_shadowing.py is a good
example of a test that earns its place. It asserts one invariant — no literal
route sits behind an earlier parameterized one — over the whole assembled
app, not one endpoint.
After a model change:
uv run alembic revision --autogenerate -m "..." # review the generated file
uv run alembic upgrade head— CLAUDE.md, "Running"
Review the autogenerated file before you apply it. Autogenerate is a starting
point, not a guarantee. If two people, or two agent sessions, create
migrations in parallel, Alembic ends up with sibling heads. Resolve that
with alembic merge, rather than picking one arbitrarily and discarding the
other's history.
server/scripts/demo*.sh (demo.sh, plus demo_webhooks.sh,
demo_permissions.sh, demo_views.sh, demo_planning.sh,
demo_automations.sh, demo_reporting.sh, demo_forms.sh,
demo_timelogging.sh) are feature walkthroughs, run over curl against a
running server:
#!/usr/bin/env bash
# End-to-end tour of the current slices: registry-driven custom fields,
# workflow states as data, labels, epic/issue/subtask hierarchy, assignment,
# comments, action RBAC + field-level visibility, event stream.
# Rerunnable (fresh workspace each run). Signs in as the seeded admin —
# override with DEMO_EMAIL/DEMO_PASSWORD.— server/scripts/demo.sh
They assume a fresh database. Each script creates fixed project keys, for
example TD, and project keys are unique instance-wide. Running one against
a database that already has real data risks a key collision. Running one
repeatedly against the same database accumulates junk projects. Point
RADD_DATABASE_URL at a throwaway database before you run a demo script. Use
pytest instead for regression coverage against a populated database.
Building is not verifying. A clean tsc and a clean vite build prove
that the code compiles, not that it renders correctly. Several real bugs
shipped past a green build because nobody looked at the output:
- A flex container squashed 92 cards into 12px strips.
- A collapse toggled
aria-expandedwhile it stayed the same width. - Board columns clipped 75 cards down to five.
The house pattern for looking is web/scripts/render-proof.mjs and its
siblings under web/scripts/ — dozens, and growing. Each drives headless
Chromium directly over the DevTools Protocol, with zero npm dependencies:
/**
* Headless render proof for the frontend module-federation platform (spec 94, LOCKED-3).
* ZERO npm deps: drives Chrome via the DevTools Protocol using Node 22's built-in WebSocket + fetch.
*
* Proves the whole chain in a REAL browser:
* 1. host boots, reads /capabilities, imports the participants REMOTE bundle at runtime;
* 2. the remote's activate() registers into the host's shared slot registry (one singleton);
* 3. the issue view's <Slot> RENDERS the remote's Participants section into the DOM;
* ...
*/
— web/scripts/render-proof.mjs
A proof takes a screenshot and probes the real DOM
(getBoundingClientRect/getComputedStyle/scrollHeight), rather than
trusting that matching CSS classes mean matching layout. Know these sharp
edges before you write one, all from CLAUDE.md's "Verifying UI work":
-
Headless Chrome reports
(hover: none)at baseline. Tailwind v4 gates everyhover:utility on@media (hover: hover). A proof that does not force hover-capable media flags shows hover-revealed chrome as permanently absent — which reads exactly like a missing feature. -
A leaked browser from an earlier run is worse than a stale bundle. Its
assertions describe code that this run never configured. The shared
openBrowserhelper refuses to start against a debug port something is already listening on. - Screenshots at reduced scale lie about small UI. Check computed styles too, not only pixels, for anything under about 16px.
Two repositories, two pipelines, and they are not interchangeable. This repo builds a container image; a separate private deployment repository decides which image runs where.
git tag -a v0.2.0 -m "Radd 0.2.0" && git push origin v0.2.0— docs/contributing.md
The pushed tag makes CI build and publish
git.radd-hq.com/radd/radd:0.2.0. Nothing runs in production yet. That
requires bumping the image tag in the deployment repository, which triggers
CD:
helm upgrade radd deploy/helm/radd -n radd -f my-values.yaml \
--set image.tag=0.2.0— docs/deploy-k3s.md
There is no latest tag. Every deployment pins an immutable version
number. A rollback edits that value back to the previous tag — never a race
over where a moving tag happens to point right now.
Never change the cluster by hand. CD reverts a kubectl edit against a
running deployment the next time it runs. The deployment repository, not the
live cluster state, is the source of truth for what should be running.
Migrations run as a pre-install/pre-upgrade Helm hook Job, ahead of the new pods:
kubectl -n radd get jobs # if a release seems to hang, check here first
— docs/deploy-k3s.md
helm rollback reverses the release, but not the migration it ran. For
anything schema-breaking, restore from a backup or a snapshot. Do not roll
back and hope the old code still matches the new schema.
main is protected: direct pushes and merges are restricted to the owner.
CI does not run on pull requests. Forgejo executes the target branch's
workflows for a fork PR, so a workflow file added inside the PR itself never
runs. Run the test suite locally before you open one.
Mirrored from project.radd-hq.com on 2026-09-12. Documentation is written there; this copy is regenerated by scripts/publish_wiki.py and hand edits do not survive it.
-
Developer guide
- Architecture: the kernel and plugins
- Develop, test and deploy
- Events and consumers
- Permissions and access control
- The MCP server
- The query language for developers
- The REST API and authentication
- Write a backend plugin
- Write a page editor extension
- Write a plugin user interface
- Write an automation node
-
Release notes
- 0.36.4
- 0.36.3
- 0.36.2
- 0.36.1
- 0.36.0
- 0.35.0
- 0.34.0
- 0.33.0
- 0.32.0
- 0.31.1
- 0.31.0
- 0.30.0
- 0.29.0
- 0.28.0
- 0.27.0
- 0.26.0
- 0.25.1
- 0.25.0
- 0.24.1
- 0.24.0
- 0.23.1
- 0.23.0
- 0.22.0
- 0.21.0
- 0.20.0
- 0.19.0
- 0.18.1
- 0.18.0
- 0.17.2
- 0.17.1
- 0.17.0
- 0.16.0
- 0.15.0
- 0.14.1
- 0.14.0
- 0.13.1
- 0.13.0
- 0.12.0
- 0.11.0
- 0.10.0
- 0.9.2
- 0.9.1
- 0.9.0
- 0.8.1
- 0.8.0
- 0.7.1
- 0.7.0
- 0.6.6
- 0.6.5
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.0
- 0.4.1
- 0.4.0
- 0.3.2
- 0.3.0
- 0.2.0
- 0.1.0
-
User guide
- AI features
- Attachments
- Automations
- Cycles and releases
- Instance settings
- Intake forms and the portal
- Notifications and the inbox
- Personal settings
- Project settings
- Projects
- Reports and dashboards
- Search and the query language
- Start here
- The application window
- The card designer
- The roadmap
- The service desk
- The wiki
- Time logging and the timesheet
- Views
- Work items