flowchart LR
A["Fork"] --> B["Clone"]
B --> C["Build<br>cargo build --workspace"]
C --> D["Test<br>cargo nextest run --workspace"]
D --> E["Make change"]
E --> F["Test again"]
F --> G["Commit<br>(conventional format)"]
G --> H["Push"]
H --> I["Open PR"]
I --> J["CI runs"]
J --> K{"CI passes?"}
K -->|yes| L["Code review"]
K -->|no| M["Fix issues"]
M --> F
L --> N{"Approved?"}
N -->|yes| O["Merge"]
N -->|changes requested| M
2 Your First Contribution
The contribution process starts with forking and ends with merging. We’ll walk through the full cycle with a concrete example: adding a minLength constraint sort to the AT Protocol.
2.1 Prerequisites
Install these tools before you begin:
| Tool | Minimum version | Install |
|---|---|---|
| Rust | 1.85+ (Edition 2024) | rustup.rs |
wasm-pack |
latest | cargo install wasm-pack |
| Node.js | 22+ | nodejs.org |
pnpm |
latest | corepack enable && corepack prepare pnpm@latest --activate |
cargo-nextest |
latest | cargo install cargo-nextest |
2.2 Step 1: fork and clone
Fork https://github.com/panproto/panproto on GitHub, then clone your fork:
git clone git@github.com:YOUR_USERNAME/panproto.git
cd panproto2.3 Step 2: understand the workspace
The repository is a Cargo workspace. Here is the workspace definition from the root Cargo.toml:
[workspace]
resolver = "3"
members = [
"crates/panproto-gat",
"crates/panproto-schema",
"crates/panproto-inst",
"crates/panproto-mig",
"crates/panproto-lens",
"crates/panproto-check",
"crates/panproto-protocols",
"crates/panproto-core",
"crates/panproto-wasm",
"crates/panproto-cli",
"crates/panproto-io",
"crates/panproto-vcs",
"crates/panproto-expr",
"crates/panproto-expr-parser",
"crates/panproto-py",
"crates/panproto-grammars",
"crates/panproto-parse",
"crates/panproto-project",
"crates/panproto-git",
"crates/panproto-llvm",
"crates/panproto-xrpc",
"crates/git-remote-cospan",Each crate under crates/panproto-* is independent. The tests/integration crate holds cross-crate integration tests. The TypeScript SDK lives in sdk/typescript/.
2.4 Step 3: build the workspace
cargo build --workspaceThis compiles all 10 crates and the integration test crate. The first build downloads dependencies and takes a few minutes. Incremental builds are fast.
The workspace uses resolver = "3" (the Rust 2024 edition resolver). Verify your Rust toolchain is 1.85 or newer: rustup update stable.
2.5 Step 4: run the test suite
cargo nextest run --workspaceAll tests should pass on a clean checkout. If any fail, check Chapter 37 for common issues and fixes.
panproto uses cargo-nextest instead of cargo test for test isolation. Each test runs in its own process, preventing shared mutable state from causing flaky results. See Chapter 3 for details.
2.6 Step 5: build the WASM target
wasm-pack build crates/panproto-wasm --target webThis compiles panproto-wasm to WebAssembly and generates JavaScript bindings in crates/panproto-wasm/pkg/. If this fails with a missing target error, run:
rustup target add wasm32-unknown-unknown2.7 Step 6: build the TypeScript SDK
cd sdk/typescript
pnpm install
pnpm buildThis compiles the TypeScript sources and runs type checking. The SDK wraps the WASM module and provides the high-level Panproto class.
2.8 Step 7: make a change
Now let us add a minLength constraint sort to the AT Protocol. Find the protocol definition in crates/panproto-protocols/src/atproto.rs and locate the section where constraint sorts are registered.
In the build_atproto_protocol() function, find the block that registers constraint sorts like maxLength, format, and enum. Add a new entry:
// In crates/panproto-protocols/src/atproto.rs
// Inside build_atproto_protocol(), in the constraint sorts section:
builder.add_constraint_sort("minLength", &[("value", "integer")])?;This registers a new constraint sort named "minLength" that takes a single parameter "value" of sort "integer".
2.8.1 Add a test
Add a test in the same file that verifies the new constraint sort is present:
#[test]
fn test_atproto_has_min_length_constraint() {
let protocol = build_atproto_protocol().unwrap();
let theory = protocol.constraint_theory();
assert!(
theory.find_sort("minLength").is_ok(),
"AT Protocol should have a minLength constraint sort"
);
}2.8.2 Run the relevant tests
cargo nextest run -p panproto-protocolsIf the constraint is used by integration tests, also run:
cargo nextest run -p panproto-integration2.9 Step 8: verify the full suite
Before committing, run the complete test suite to ensure nothing else broke:
cargo nextest run --workspaceChanges to protocol definitions can affect schema validation, migration compilation, and integration tests. A passing per-crate test does not guarantee the full suite passes.
2.10 Step 9: commit and push
panproto uses Conventional Commits. Create a feature branch and commit with the appropriate prefix:
git checkout -b feat/atproto-min-length
git add crates/panproto-protocols/src/atproto.rs
git commit -m "feat(protocols): add minLength constraint sort to AT Protocol"
git push origin feat/atproto-min-lengthCommon prefixes:
| Prefix | Use for |
|---|---|
feat |
New feature or capability |
fix |
Bug fix |
refactor |
Code restructuring with no behavior change |
test |
Adding or updating tests |
docs |
Documentation changes |
perf |
Performance improvement |
chore |
Build, CI, dependency updates |
2.11 Step 10: open a pull request
Go to your fork on GitHub and open a pull request against main. The PR template guides you through:
- Description: what the change does and why.
- Testing: how you verified it works.
- Breaking changes: whether any public API changed.
2.12 What happens next
CI runs automatically after you push:
- Lint:
cargo clippy --workspace -- -D warningswith pedantic and nursery lints. - Test:
cargo nextest run --workspaceon Linux, macOS, and Windows. - WASM:
wasm-pack buildandwasm-pack test --headless --chrome. - TypeScript:
pnpm build && pnpm testinsdk/typescript/. - Format:
cargo fmt --checkandprettier --checkfor TypeScript.
CI must pass before a maintainer reviews your PR. If a CI job fails, check the logs. Chapter 37 covers the most common failure modes and how to reproduce them locally.
2.13 The contribution lifecycle
2.14 Checklist
Before opening your PR, verify:
2.15 Types of contributions
Good first issues are labeled on the issue tracker. These are scoped, well-described tasks suitable for newcomers.
Bug fixes start with a test that reproduces the bug. Fix the code, verify the test passes. Use fix(crate): description as the commit prefix.
New protocol support means implementing the Protocol trait in panproto-protocols. See chapters 7 and 12 for what a protocol requires.
Performance improvements start with benchmarks (chapter 4). Identify a bottleneck, optimize, and verify with before/after numbers. Use perf(crate): description as the commit prefix.
Documentation improvements to rustdoc, this guide, or the tutorial are always welcome. Use docs: description as the commit prefix.
2.16 What’s next
The next two chapters cover the build system and debugging tools in more depth. When you are ready to understand the code you are changing, jump to the architecture chapters (Part III). For the complete coding conventions and PR expectations, see chapters 16–18.