Contributing
Guidelines for contributing to SystemPrompt
On this page
Thank you for your interest in contributing to SystemPrompt. This guide covers our development process and coding standards.
Getting Started
Prerequisites
- Rust 1.75+
- Docker and Docker Compose
- Git
- Just command runner (optional)
Setup
# Clone the repository
git clone --recursive https://github.com/systempromptio/systemprompt-core
cd systemprompt-core
# Start database
docker-compose up -d
# Run migrations
cargo run -p systemprompt-cli -- infra db migrate
# Build all crates
cargo build
# Run tests
cargo test
Code Organization
Crate Structure
SystemPrompt uses a layered crate architecture:
crates/
├── shared/ # Core types (models, traits, identifiers)
├── infrastructure/ # Database, logging, config, events
├── domain/ # Business logic (users, agents, mcp)
├── application/ # Runtime, scheduler, generator
├── entry/ # API server, CLI
└── facade/ # Public API facade
Dependency Rules
- Dependencies flow downward only
- Same-layer crates communicate via traits
- No circular dependencies
- Extensions in downstream projects, not core
Coding Standards
Rust Guidelines
Required:
// Use explicit error types
fn process() -> Result<Output, ProcessError> { ... }
// Handle all errors
let value = operation().map_err(|e| ProcessError::Operation(e))?;
// Document public items
/// Processes the input and returns formatted output.
///
/// # Errors
/// Returns `ProcessError::InvalidInput` if input is malformed.
pub fn process(input: &str) -> Result<String, ProcessError> { ... }
Forbidden (lint denies):
// Never use unwrap
let value = option.unwrap(); // DENIED
// Never panic
panic!("unexpected"); // DENIED
// Never use todo
todo!("implement later"); // DENIED
SQL Guidelines
Required:
- All SQL in
.sqlfiles - Compile-time verification via SQLx
- Parameterized queries only
// Correct: SQL in file, compile-time verified
let users = sqlx::query_file_as!(User, "queries/get_users.sql")
.fetch_all(&pool)
.await?;
// Wrong: Inline SQL string
let users = sqlx::query_as!(User, "SELECT * FROM users") // AVOID
Error Handling
Use thiserror for error types:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ServiceError {
#[error("database error: {0}")]
Database(#[from] sqlx::Error),
#[error("not found: {0}")]
NotFound(String),
#[error("validation failed: {0}")]
Validation(String),
}
Pull Request Process
Before Submitting
-
Run all checks:
cargo fmt --all cargo clippy --all-targets --all-features cargo test --all -
Update documentation if changing public API
-
Add tests for new functionality
-
Update CHANGELOG with your changes
PR Guidelines
- Keep PRs focused on a single change
- Write descriptive commit messages
- Reference related issues
- Respond to review feedback promptly
Commit Messages
Follow conventional commits:
type(scope): description
feat(mcp): add tool timeout configuration
fix(auth): handle expired refresh tokens
docs(readme): update installation instructions
refactor(database): simplify connection pooling
test(agents): add integration tests for messaging
Types: feat, fix, docs, refactor, test, chore
Testing
Unit Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validation() {
let input = "valid";
assert!(validate(input).is_ok());
}
#[test]
fn test_invalid_input() {
let input = "";
assert!(matches!(
validate(input),
Err(ValidationError::Empty)
));
}
}
Integration Tests
#[tokio::test]
async fn test_user_creation() {
let pool = test_database().await;
let service = UserService::new(pool);
let user = service.create("test@example.com").await.unwrap();
assert_eq!(user.email, "test@example.com");
}
Running Tests
# All tests
cargo test
# Specific crate
cargo test -p systemprompt-database
# With output
cargo test -- --nocapture
# Integration tests only
cargo test --test '*'
Documentation
Code Documentation
Document all public items:
/// A user in the system.
///
/// Users can authenticate via OAuth or WebAuthn and have
/// associated roles that determine their permissions.
///
/// # Example
///
/// ```rust
/// let user = User::new("user@example.com");
/// assert!(user.is_active());
/// ```
pub struct User {
/// Unique identifier.
pub id: UserId,
/// Email address (unique).
pub email: String,
/// Display name.
pub name: Option<String>,
}
Playbooks
Update playbooks when changing workflows:
# View playbook
systemprompt core playbooks show build_extension-checklist
# Sync after changes
systemprompt core playbooks sync --direction to-db -y
Extension Development
When building extensions:
- Create crate in
extensions/directory - Implement required traits
- Register with
register_extension! - Add to workspace
Cargo.toml
See Building Extensions for details.
Getting Help
- Questions: Open a GitHub Discussion
- Bugs: Open a GitHub Issue
- Security: Email security@systemprompt.io
License
By contributing, you agree that your contributions will be licensed under the FSL-1.1-ALv2 license.