Contributing
The full contributor guide lives in
CONTRIBUTING.md
at the repository root. This page is the short version and the
pointers.
The gate
just check
just check-feature-configs
cargo fmt --check, the Unicode punctuation gate, the scripts’ own tests,
the shell crate’s rustfmt
check (client-tauri-fmt), the client’s
Prettier check, ESLint, svelte-check and
tsc --noEmit and then its vitest run, the shell crate’s clippy and tests
(client-tauri-clippy, client-tauri-test), its android
warnings check (client-android-warnings),
clippy with warnings denied, cargo nextest run --workspace --all-features,
cargo test --doc --workspace --all-features, and cargo doc. CI’s set
minus its cargo check --release job, the client build’s
scripts/check_client_wasm.py step (just check builds no frontend), and
its install-and-boot proof (scripts/install.sh, then a boot of the
installed binary with no library path, which neither local command runs),
plus the android warnings check CI cannot run yet
(client-android-warnings; its vendoring refuses non-macOS hosts - #1959,
closed as an enhancement and tracked in #2258). CI also runs the workspace
clippy on a macOS runner, which lints the cfg(target_os = "macos") arms
a Linux run compiles out; just check covers them only when it runs on
macOS.
The gate is two commands: just check, and just check-feature-configs,
which compiles the configuration install.sh builds. Both green before
you open a pull request. just check runs its four independent groups
concurrently - the fast checks, the Rust workspace, the client web steps
and the shell crate’s steps - capped by FORGE_CHECK_JOBS (default 4; set
it lower on a smaller machine). Every group runs to its own first failure,
a failing group’s log tail is printed, and the verdict names the first
failing step; each run’s per-step logs stay in their own
target/check-logs/run.XXXXXX/ directory, printed as logs: ... before
the verdict.
just check-serial is the same gate one step at a time, for debugging.
When sccache is on PATH, the recipes export RUSTC_WRAPPER=sccache so
compiled objects are shared across worktrees and branches; without it they
build normally.
For a review round, just check-delta <crate>... runs the fast slice on
the crates a change touches: their rustfmt check, the Unicode gate, both
clippy feature sets and nextest, plus the client’s steps or the shell
crate’s clippy and tests when client or client-tauri is named. It does
not replace the gate; the final head runs the full two commands.
CI runs each build and test job in ci.yml only for the paths it serves,
and one job, ci-required, always runs, depends on every job in that
workflow, and fails when a job that ran failed or when a job whose paths
changed was skipped. The ruleset requires two checks: that aggregate and
build site, the docs workflow’s build, which runs on every pull request.
A fresh worktree has no client/node_modules; each client step installs it
first when it is missing or older than package-lock.json, so the first
just check there runs whole rather than stopping at the Prettier step.
The shell under client/src-tauri/ is its own workspace root, so the
workspace’s cargo steps do not reach it; in CI its clippy and tests are the
client-tauri job’s, and its rustfmt check the cargo fmt --check job’s
second invocation. just check
reaches it through client-tauri-fmt (the shell crate’s own cargo fmt --check), the crate’s own clippy and tests (client-tauri-clippy and
client-tauri-test) and client-android-warnings (the crate compiled for
aarch64-linux-android with warnings denied, which is what catches
desktop-only code going dead on Android); the Unicode punctuation gate and
the client’s Prettier step reach it too.
just client-tauri-check builds the shipping configuration (and runs the
tests) and just client-tauri-bundle adds the bundles.
just client-android-check
compiles the Kotlin half and runs its unit tests, because nothing else
reaches that code; it needs the Android SDK and a JDK.
The last line it prints is its verdict, [OK] check: ... or
[ERROR] check: <step> failed, or [ERROR] check: group reported nothing (<group>) when a group died without a report. A failing group stops at
its own first failure; the other groups still run; every failing group’s
log tail is printed before the verdict; and the verdict names the first
failing step and the later steps in that group that did not run. Read that line rather
than a pipeline’s exit status: piping just check through tail reports
tail’s status, not the recipe’s.
The book’s own gate
docs/book/ui-word-count.sh measures every
docs/book/src/ui/client/*.md page against its recorded prose count in
docs/book/ui-word-count.baseline, ignoring mockups and collapsed
blocks. A page may shrink freely; a page over its baseline, missing from
the baseline, or a baseline entry whose page is gone fails the gate.
Growing, adding or removing a page is a deliberate baseline edit in the
same pull request, and --record rewrites the baseline from the current
pages. just ui-word-count runs the gate, and just check runs that
step.
The rules that bite first
- Clippy runs at
pedantic, denied.unwrap,expect,panic,exit,todoandunimplementedare denied, andunsafe_codeis forbidden workspace-wide. The client shell crate, a workspace root of its own, mirrors the table and deniesunsafe_codewith scoped allows in the three FFI modules.clippy.tomlrelaxesunwrap,expectandpanicin tests; the other three stay denied everywhere. - No
mod.rs.foo.rssits next tofoo/. WARNandERRORare for forge’s own problems. A session’s own work, its tool call failing or its command exiting non-zero, is information about that session and belongs atdebug. A line that does claim a problem names the session, the org, the model, the account or the path, whichever apply, and carries anevent_name.- No em-dashes, en-dashes, horizontal bars or curly quotes across
the scanned source, docs and config file types. CI rejects them.
Ellipsis is allowed.
just unicode-punct-checkshows what would be flagged. - New wire surface ships with a captured baseline. See the wire contract.
- UI changes update the UI surface pages in the same pull request.
- Changes that make a page here false update it in the same pull
request. This site deploys on every push to
main, so a page left behind is republished wrong within minutes of the merge.
Where does my code go?
The architecture page has the placement guide. Bias toward the deeper crate when unsure.
Commits and pull requests
Short imperative subjects saying what the commit does. One logical unit per commit. Version bumps in their own commit. Pull request descriptions are prose, as short as the change allows, linking the issue they close.