Split agent and contributor docs by project

This commit is contained in:
Rūdolfs Ošiņš
2026-10-22 11:06:33 +02:00
parent fd4436bb8f
commit 6c0ddd64dc
9 changed files with 264 additions and 401 deletions
+21 -158
View File
@@ -1,145 +1,28 @@
# radicle-gui
# radicle-explorer
Monorepo for Radicle's graphical interfaces. It holds two independent
projects, each with its own npm package, Cargo workspace, toolchain files,
checks and conventions:
The Svelte web app for browsing Radicle repositories, its TypeScript HTTP
client, and the `radicle-httpd` and `radicle-search` backends it talks to.
- **Explorer** (repository root): the Svelte web app for browsing Radicle
repositories, its TypeScript HTTP client, and the `radicle-httpd` and
`radicle-search` backends it talks to.
- **Desktop** (`desktop/`): the Tauri desktop app. Its own `desktop/CLAUDE.md`
is the source of truth for anything under `desktop/`.
The project lives in `explorer/` of the radicle-gui monorepo. It is its own
npm package and Cargo workspace: run every command here from `explorer/`,
and all paths in this file are relative to it. Conventions shared with
desktop are in the root `AGENTS.md`.
## Layout
| Path | Project | What it is |
|---|---|---|
| `src/`, `public/`, `tests/` | explorer | Svelte web app and its unit, e2e and visual tests |
| `http-client/` | explorer | TypeScript client for the httpd API |
| `crates/radicle-httpd/` | explorer | HTTP daemon serving the API (Rust), with its own `CLAUDE.md` for code conventions |
| `crates/radicle-search/` | explorer | Search service, released together with httpd (Rust) |
| `workers/` | explorer | Cloudflare workers (open-graph images) |
| `scripts/` | explorer | Check, CI and e2e helper scripts |
| `desktop/` | desktop | Tauri app: Svelte frontend in `desktop/src/`, Rust in `desktop/crates/` |
| `.github/workflows/` | both | GitHub CI: `explorer-*.yml` for explorer, `desktop-*.yml` for desktop |
| `.buildkite/` | explorer | Radicle CI pipelines |
| `.ryph/` | both | Dev machine image and setup for agents, see `.ryph/environment.md` |
## Working in this repo
- Work out which project a change belongs to first, and run every command
from that project's root: the repository root for explorer, `desktop/`
for desktop. Root `npm` and `cargo` commands never cover `desktop/`, and
the root lint and format checks skip it on purpose.
- The two projects don't share code. A change that touches both is two
changes: check each project with its own commands.
- The rules under "Explorer" below don't apply in `desktop/`. In particular,
explorer uses legacy Svelte syntax while desktop uses Svelte 5 runes.
### Plan mode
- Propose the minimal MVP first — no tests, no docs, no speculative extras
- Tests are added only when explicitly requested after MVP review
- Keep plans concise: short bullet points listing what changes and where
- Skip obvious details and boilerplate explanations
### Shared conventions
- Prefer `undefined` over `null`
- Do not add comments unless explicitly asked. When writing comments,
use proper English sentences
- Ask before adding new dependencies
- Use npm, never `yarn` or `pnpm`
- Do not use `npm test` — neither project has a default test script
- Do not use `npx vitest` or `npx playwright test` — use the `npm run` scripts
### Commit messages
- Imperative mood: "Add feature" not "Added feature"
- Capitalize subject, no trailing period, max 50 chars
- Prefix backend-only commits with `httpd:`
### Patches
Label every new patch with each component it touches, so reviewers and
filters can tell the projects apart:
| Label | Covers |
|---|---|
| `explorer` | the web app at the root: `src/`, `http-client/`, `workers/`, its tests and config |
| `desktop` | anything under `desktop/` |
| `httpd` | `crates/radicle-httpd/` and `crates/radicle-search/` |
A patch that touches several components gets several labels. Changes to
shared files (`.github/`, `.buildkite/`, `.ryph/`, root docs) get the labels
of the components they affect. `git push` can't set labels, so add them
once the patch is open:
```sh
rad patch label <patch-id> --add explorer --add httpd
```
### Releases and tags
Each project is released on its own. Release tags are
`releases/<component>/<version>`; the `refs/tags/releases/*` rule in the
repository's identity decides when they become canonical.
| Component | How it's released | Tag |
|---|---|---|
| Explorer web app | Deployed continuously, no releases or tags | — |
| `radicle-httpd` + `radicle-search` | Together, one version, see `RELEASE.md` | `releases/httpd/<version>` |
| Desktop app | `desktop/scripts/release`, see `desktop/README.md` | `releases/desktop/<version>` |
The older `releases/<version>` tags (httpd 0.22.0–0.29.0, desktop
0.9.0–0.16.0) are left over from before the two repositories were merged.
Never create, match or describe against them; scripts must always match a
component's own prefix.
### Radicle ecosystem (sibling repos)
See the `radicle-ecosystem` skill for the map of sibling repositories
(heartwood, radicle-git, radicle-job, rips, radicle.dev) and the key
crates and files in each.
### Domain glossary
- **RID** — Repository ID (`rad:z3gqc...`)
- **DID** — Decentralized Identifier, user identity (`did:key:z6Mk...`)
- **NID** — Node ID, public key suffix of a DID
- **COB** — Collaborative Object (issue, patch, or identity as a Git DAG)
- **Delegate** — authorized repo maintainer; signatures determine canonical state
- **Patch** — pull-request equivalent with immutable revisions and reviews
- **Seed** — hosting/replicating a repo; seed nodes are always-on servers
- **Canonical refs** — branches/tags resolved by delegate quorum
## Explorer (repository root)
Everything in this section applies to the repository root only. Run all
commands from there.
### Tech stack
- Svelte 5 with legacy syntax (`$:`, stores, `export let`). Do not use
Svelte 5 runes (`$state`, `$derived`, `$props`) here
- TypeScript, Vite, Vitest, Playwright, Zod
- Rust for `crates/radicle-httpd` and `crates/radicle-search`
### Checks and linting (tsc, svelte-check, eslint, prettier)
## Checks and linting (tsc, svelte-check, eslint, prettier)
```sh
npm run check
npm run format # auto-fix prettier issues
```
### Unit tests
## Unit tests
```sh
npm run test:unit
npm run test:http-client:unit
```
### E2E tests
## E2E tests
```sh
npm run test:e2e -- --project chromium
@@ -155,7 +38,7 @@ or test fixtures requires a full run.
SKIP_SETUP=true npm run test:e2e -- --project chromium
```
### Rust backend (`crates/`)
## Rust backend (`crates/`)
The same commands CI runs:
@@ -176,7 +59,7 @@ npm run test:e2e:local -- --project chromium
npm run test:http-client:unit:local
```
### API changes across layers
## API changes across layers
Adding or changing an API endpoint typically touches three layers:
1. `crates/radicle-httpd/` — Rust handler (add route, types, logic)
@@ -188,49 +71,29 @@ or remove fields/endpoints unless explicitly deprecating. The frontend
must handle older nodes gracefully: treat new API fields as optional
and fall back when they're absent.
### Pre-push checklist
## Pre-push checklist
`npm run check:ci` runs everything below except the `:local` variants, plus
the build smoke test, from the GitHub workflows, and stops at the first
failure. It logs each step's output, duration and exit code to
`tests/tmp/check-ci.log`.
`npm run check:ci` runs the checks, unit, http-client, e2e and build smoke
tests from the GitHub workflows, and stops at the first failure. It logs
each step's output, duration and exit code to `tests/tmp/check-ci.log`. If
Rust code changed, also run the Rust commands and the `:local` variants
above.
1. `npm run check`
2. `npm run test:unit`
3. `npm run test:http-client:unit`
4. `npm run test:e2e -- --project chromium`
5. If Rust code changed: clippy, fmt, cargo test (see above),
plus `:local` variants of e2e and http-client tests
## Svelte components
### Svelte components
- Script order (enforced by prettier): `<script context="module">`,
`<script lang="ts">`, `<style>`, markup
- Optional props use `export let foo: T = undefined`, not `?:`
- CSS: scoped styles with design tokens (`var(--color-text-primary)`,
`var(--txt-body-m-regular)`, `var(--border-radius-sm)`); use `:global()`
for styling slotted or `{@html}` content
- Loading states: `{#await promise}` blocks for inline async;
local `loading` boolean with `try/catch` for imperative fetches
### `{@html}` and untrusted data (XSS)
## `{@html}` and untrusted data (XSS)
- Avoid `{@html}` with backend-derived data (branch/ref names, aliases,
descriptions); when unavoidable, its argument must be DOMPurify-sanitized
(`sanitizeConfig` in `src/lib/markdown.ts`) or built from `escape()`'d
parts, in the component that renders it.
### TypeScript / HTTP client
## TypeScript / HTTP client
- Derive types from Zod schemas (`z.infer<typeof fooSchema>`), never
hand-write interfaces that duplicate a schema
- Schema naming: `fooSchema` (camelCase + Schema suffix) → type `Foo`
- Use ES private fields (`#field`), not the TypeScript `private` keyword
- API timestamps are in seconds; multiply by 1000 for JS `Date`
- Relative imports inside `http-client/` use `.js` extension (ESM)
## Desktop (`desktop/`)
See `desktop/CLAUDE.md`. In short: Tauri with a Svelte 5 frontend that uses
runes, a hexagonal Rust backend in `desktop/crates/`, and its own checks
(`npm run check:ci` from `desktop/`).
-145
View File
@@ -1,145 +0,0 @@
# CONTRIBUTING
Contributions are very welcome. When contributing code, please follow these
simple guidelines.
* Make sure you run `npm run check` for code formatting problems.
* Run our test suite `npm run test:*` to avoid regressions or bugs.
* Before adding any code dependencies, check with the maintainers if this is okay.
* Write properly formatted comments: they should be english sentences, eg:
// Return the current UNIX time.
* Follow the guidelines when proposing code changes (see below).
* Write properly formatted git commits (see below).
Running Tests
-------------
The test suite includes end-to-end (e2e) tests that run against the built
application.
**Basic usage:**
npm run test:e2e
**Skipping setup:**
The test suite performs setup operations before running tests:
* Building the application bundle
* Creating test fixtures (repositories with test data)
On subsequent test runs, you can skip this setup to save time:
SKIP_SETUP=true npm run test:e2e
Use this when:
* You haven't changed any source code since the last test run
* The test fixtures are already created
* You want to iterate quickly on test development
**Note:** If you've made code changes or are getting unexpected test failures,
remove the `SKIP_SETUP` flag to ensure tests run against the latest build.
**Common usage patterns:**
* First time run: `npm run test:e2e -- --project chromium`
* Subsequent runs (no code changes): `SKIP_SETUP=true npm run test:e2e -- --project chromium`
* Single test by line number: `SKIP_SETUP=true npm run test:e2e -- tests/e2e/repo/commits.spec.ts:90 --project chromium`
**Testing with local radicle-httpd build:**
If you're developing radicle-httpd and want to test changes locally:
npm run test:e2e:local
This will:
1. Compile radicle-httpd from the `radicle-httpd/` directory
2. Run the full test suite against the locally compiled binary
**Running e2e tests in Docker:**
If your host OS isn't supported by Playwright's browser builds (e.g. a very
new or unusual Linux distribution), run the suite inside a container instead.
Requires Docker with the Compose plugin.
scripts/test-e2e-docker
Any arguments are forwarded to `npm run test:e2e` (defaulting to
`--project chromium` when none are given):
scripts/test-e2e-docker tests/e2e/repo.spec.ts
scripts/test-e2e-docker --project chromium -g "some test name"
`SKIP_SETUP=true` is forwarded the same as for the native runner:
SKIP_SETUP=true scripts/test-e2e-docker
To test against a radicle-httpd you compile from source (the equivalent of
`npm run test:e2e:local`), pass `--local`:
scripts/test-e2e-docker --local
Notes:
* The first image build is slow (it downloads Chromium); subsequent runs are
cached. `--local` additionally installs a Rust toolchain and compiles
radicle-httpd on first use.
* The repo is bind-mounted, so test artifacts and the binary/fixture cache
land on the host as usual. `node_modules`, the cargo cache, and httpd's
build directory live in container-private volumes, leaving your host copies
untouched.
Proposing changes
-----------------
When proposing changes via a patch:
* Isolate changes in separate commits to make the review process easier.
* Don't make unrelated changes, unless it happens to be an obvious improvement to
code you are touching anyway ("boyscout rule").
* Rebase on `master` when needed.
* Keep your changesets small, specific and uncontroversial, so that they can be
merged more quickly.
* If the change is substantial or requires re-architecting certain parts of the
codebase, write a proposal in english first, and get consensus on that before
proposing the code changes.
Writing Git commit messages
---------------------------
A properly formed git commit subject line should always be able to complete the
following sentence:
If applied, this commit will _____
For example, the following message is well formed:
Add support for .gif files
In addition, it should be capitalized and *must not* include a period.
When it comes to formatting, here's a model git commit message[1]:
Capitalized, short (50 chars or less) summary
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.
Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
or "Fixes bug." This convention matches up with commit messages generated
by commands like git merge and git revert.
Further paragraphs come after blank lines.
- Bullet points are okay, too.
- Typically a hyphen or asterisk is used for the bullet, followed by a
single space, with blank lines in between, but conventions vary here.
- Use a hanging indent.
---
[1]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
+28 -5
View File
@@ -188,12 +188,35 @@ git clone https://seed.example.com/heartwood.git
```
## Development
Run `npm run check` before proposing a patch, along with the test suites:
`npm run test:unit`, `npm run test:http-client:unit` and `npm run test:e2e`.
E2E tests build the app and create fixtures before running. If neither the
source nor the fixtures changed since the last run, skip that with
`SKIP_SETUP=true`:
```sh
npm run test:e2e -- --project chromium
SKIP_SETUP=true npm run test:e2e -- tests/e2e/repo/commits.spec.ts:90 --project chromium
```
When changing `radicle-httpd`, `npm run test:e2e:local` compiles it from
`crates/radicle-httpd/` and runs the suite against that build instead of
the released binary.
If Playwright's browsers don't support your OS, `scripts/test-e2e-docker`
runs the suite in a container (Docker with the Compose plugin). It forwards
its arguments and `SKIP_SETUP` to `npm run test:e2e`, and `--local` tests
against httpd built from source. The first run downloads Chromium and is
slow.
## Contributing
* For detailed contribution guidelines, refer to the [CONTRIBUTING.md][con]
file
* To propose changes, open an [issue][iss] or submit a [patch][pat] using
Radicle
See [CONTRIBUTING.md][con] for how to propose changes and write commit
messages. Open an [issue][iss] or a [patch][pat] using Radicle.
## Getting in touch
@@ -209,7 +232,7 @@ The UI is distributed under the terms of GPLv3. See [LICENSE][lic] for details.
[app]: https://radicle.network
[con]: ./CONTRIBUTING.md
[con]: ../CONTRIBUTING.md
[def]: ./config/default.json
[env]: ./config/custom-environment-variables.json
[iss]: https://radicle.network/nodes/iris.radicle.network/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5/issues
+1 -1
View File
@@ -1,7 +1,7 @@
# radicle-httpd
Rust backend for the Radicle web explorer, released together with
`radicle-search` (see `RELEASE.md` at the repository root).
`radicle-search` (see `RELEASE.md` in `explorer/`).
## Code conventions
+1 -1
View File
@@ -72,7 +72,7 @@ export default async function globalSetup(): Promise<() => void> {
// Evaluated once at startup; captured by async setup operations.
// Set SKIP_SETUP=true to skip both build and fixture creation on subsequent runs.
// See CONTRIBUTING.md for details.
// See README.md for details.
const shouldSetup = !process.env.SKIP_SETUP;
if (shouldSetup) {