Skip to content

Contributing to GraphForge

Thank you for your interest in contributing to GraphForge!

GraphForge is a Rust core with thin Python and Node bindings. These docs target v0.6.0; see installation for artifact availability. Develop and verify from source on main for engine and binding work.

Branch Role
main Current product line (Rust core, Arrow results, Parquet projects, analyst verbs)

Next steps for contributors: set up the environment below → run the validation suite → open a focused PR against main. For release operators, start at Publishing and RELEASING.md at the repository root.


Prerequisites: Python 3.10+, Rust stable (pinned by rust-toolchain.toml), uv, maturin, pnpm for the Node binding, and cargo-nextest. Cargo with nextest is the CI Rust compile/test authority (ADR 0048); the exact CI commands are in agent-environment.md.

Terminal window
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update stable
# Install cargo-nextest
cargo install --locked cargo-nextest
git clone https://github.com/CurateLabs/graphforge.git
cd graphforge
# Install Python dev dependencies
uv sync --dev
# Build and install the native Python extension
maturin develop --release -m crates/graphforge-bindings-py/Cargo.toml
# Verify
cargo test --workspace
python -c "import graphforge; print(graphforge.__version__)"
make check

See Installation for the published-package path.


Run the targeted tests for the changed surface, then make check before pushing:

Terminal window
cargo fmt --all -- --check
cargo clippy --workspace -- -D warnings
make check # Python format/lint/type-check/security, workflow lint, repo-checks

make check mirrors the CI Lint job. For Rust: run make test-rust (or narrow with make test-rust ARGS="-p <crate>"). For Python/Node bindings: make test-python / make test-node. The CI Gate Rust lane’s exact nextest commands are in agent-environment.md.

Terminal window
# Rust
cargo test --workspace # all crates
cargo test -p graphforge-cypher # one crate
cargo test --workspace -- --nocapture # with output
# Python binding / workspace checks
make test
make test-unit
Terminal window
# Rust
cargo fmt --all
cargo clippy --workspace -- -D warnings
# Python tooling (ruff / ty via make targets)
make format
make lint
make type-check

Python type checking uses the pinned ty development dependency. make type-check, make check, and the local pre-commit ty hook use the same pyproject.toml configuration: Python 3.10 and the public binding package, including _graphforge_rs.pyi. Run uv sync --locked --all-extras to install the development environment first. The existing CI lint job runs this check; pre-commit.ci leaves it to that job because the hook needs the project environment.

The migration retains unsafe-return, redundant-cast, unused-ignore, and redundant condition diagnostics. Ty checks unannotated function bodies by default. Its comparison and unreachable-code coverage currently differs from mypy: the redundant-condition rules cover boolean conditions, but do not replace all of mypy’s strict_equality and warn_unreachable diagnostics. See Astral’s migration guide for the rule mapping. Ty’s strict-equality-semantics option controls inference and narrowing; it is not an equivalent of mypy’s strict_equality check.


graphforge/
├── crates/ # Rust workspace
│ ├── graphforge-api/ # public Rust facade
│ ├── graphforge-cypher/ # openCypher parser
│ ├── graphforge-ir/ # graph IR
│ ├── graphforge-rel/ # relational lowering
│ ├── graphforge-exec/ # execution + analyst verbs
│ ├── graphforge-storage/ # project generations, Arrow schemas, Parquet storage
│ ├── graphforge-knowledge/ # knowledge + epistemic record domains
│ ├── graphforge-bindings-py/ # PyO3 Python binding
│ ├── graphforge-bindings-node/ # napi-rs Node binding
│ └── …
├── packages/ # Node packaging and agent skills
├── docs/ # Markdown sources (Starlight syncs an allowlist)
├── docs-site/ # Astro Starlight site
├── tests/
├── Cargo.toml
└── pyproject.toml # workspace tooling (not the published wheel)

The published graphforge wheel is built from crates/graphforge-bindings-py. Python and Node are thin bindings — never fallback engines.


Prefer Rust tests colocated with the module under test for core behavior. Binding tests exercise the thin adapter surface only.

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_scan_op() {
let op = GraphOp::NodeScan { var: VarId(0), ty: TypeId(1) };
assert!(matches!(op, GraphOp::NodeScan { .. }));
}
}
  • Fast where isolation allows
  • Isolated: no shared mutable state between tests
  • Deterministic: same input = same output
  • Named descriptively

See ../engineering/TESTING.md for the release-prep testing strategy (layered gates, TCK posture, binding release-candidate evidence), and testing.md for command recipes and suite layout.


  • cargo fmt enforced in CI
  • cargo clippy -- -D warnings enforced in CI
  • No #[allow(dead_code)] without explanation
  • Public items need doc comments
  • Thin adapters only — no semantic ownership in the binding layer
  • Type hints / typed APIs on public surfaces
  • No # type: ignore without explanation
  • First-party TypeScript compiler/loader policy: typescript-toolchain.md

Keep each PR focused on one issue and one concern, with the tests needed to prove its acceptance criteria. Size is advisory: split XL work or independently reviewable concerns when the review benefit justifies another CI cycle.

Good:

  • Single feature or bug fix
  • Clear, focused purpose
  • Acceptance criteria covered by tests or deterministic evidence

Too large:

  • Multiple unrelated changes
  • Refactoring + new feature + bug fixes combined

For example, a bounded Cypher feature may need parser, IR, lowering, execution, and regression-test changes in one PR to demonstrate working behavior. Do not split solely at crate boundaries or defer a change’s acceptance tests to a later PR. When a split is justified, each PR must have independently verifiable acceptance criteria, and dependencies must be explicit.

Finish and merge reviewed work before starting more implementation. Coordinated agent teams have a limit of six unmerged changes across all agents, including draft PRs and implemented local branches. The coordinating agent owns the merge queue; prioritize existing PRs and shared CI blockers, and integrate dependent changes in order. See the work-in-progress rules in AGENTS.md for counting, the bounded blocker exception, and handling an inherited backlog.

Fix problems properly, not with temporary workarounds. Investigate root causes, add regression tests, and keep CI checks enabled.

All PRs must:

  • Pass required CI checks for the changed surface
  • Include tests or deterministic evidence for new behavior
  • Update relevant documentation
  • Have a clear description
  • Reference the issue number in the commit and PR body (Closes #XX or Refs #XX)

See AGENTS.md for agent workflow, agent-environment.md for sandbox and native-binding caveats, and CONTRIBUTING.md for contribution, conduct, and licensing onboarding contract.


  1. Spec-driven correctness — openCypher semantics over performance
  2. Arrow as the data-plane wire contract — Cypher and analyst/data-bearing results cross language boundaries as Arrow RecordBatch streams; control/metadata/lifecycle/explanation/construction may return scalars, collections, unit, or handles
  3. GraphForge owns the semantics — no binding or storage provider becomes the semantic owner
  4. Surfaces stay independent — analyst verbs bypass the Cypher parser; they do not rewrite it
  5. Inspectable — explain at every compiler stage; structured errors with spans

When implementing openCypher features:

  1. Check the TCK coverage matrix and related conformance docs under docs/reference/
  2. Mark features as supported, planned, or unsupported as appropriate
  3. Add corresponding TCK / regression coverage
  4. Ensure semantic correctness per the openCypher specification

Supported features must pass their TCK scenarios — this is a hard merge gate.


  • Rust: doc comments (///) on all public items; cargo doc must build cleanly
  • Bindings: document only the thin public adapter surface

When adding features, update:

  • docs/book/architecture/ — if the change affects the compiler pipeline, storage, or execution model
  • docs/reference/ — if the public API changes

Method and hashes live in the repo; results live on the issue. A development page records how to reproduce a measurement and the content digests that identify its inputs. Raw output (JSON, logs, receipts, per-run tables) is attached to the issue or pull request that produced it, or to a release artifact when a release claim depends on it, and is never committed under docs/. scripts/ci/docs-tree-policy.py fails CI on evidence-shaped or oversize files under docs/ and on any docs/development/*.md that no other tracked file references; a page named after an issue is folded into its topic page or deleted when the issue closes. docs/ is the only hand-maintained source: the published site renders an allowlisted subset and keeps no copies.


GraphForge follows Semantic Versioning. The documentation targets v0.6.0 (see installation for artifact availability). Before v1.0.0, backward compatibility and migration support are not guaranteed.

See RELEASING.md at the repository root for the release procedure and roadmap.md for delivery sequencing.


GraphForge is open source under the Apache License 2.0 (Apache-2.0). Under Section 5 of that license, intentionally submitted contributions are provided under Apache-2.0 unless explicitly stated otherwise. Contributors retain ownership and must have the right to submit their work; contributions made within the scope of employment require employer authorization.