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 panproto

2.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 --workspace

This compiles all 10 crates and the integration test crate. The first build downloads dependencies and takes a few minutes. Incremental builds are fast.

Note

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 --workspace

All tests should pass on a clean checkout. If any fail, check Chapter 37 for common issues and fixes.

TipWhy nextest?

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 web

This 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-unknown

2.7 Step 6: build the TypeScript SDK

cd sdk/typescript
pnpm install
pnpm build

This 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-protocols

If the constraint is used by integration tests, also run:

cargo nextest run -p panproto-integration

2.9 Step 8: verify the full suite

Before committing, run the complete test suite to ensure nothing else broke:

cargo nextest run --workspace
ImportantAlways run the full suite

Changes 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-length

Common 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:

  1. Description: what the change does and why.
  2. Testing: how you verified it works.
  3. Breaking changes: whether any public API changed.

2.12 What happens next

CI runs automatically after you push:

  1. Lint: cargo clippy --workspace -- -D warnings with pedantic and nursery lints.
  2. Test: cargo nextest run --workspace on Linux, macOS, and Windows.
  3. WASM: wasm-pack build and wasm-pack test --headless --chrome.
  4. TypeScript: pnpm build && pnpm test in sdk/typescript/.
  5. Format: cargo fmt --check and prettier --check for TypeScript.
Note

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

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
Figure 2.1: End-to-end contribution workflow

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.