Contributing to Mimir
Welcome to the Mimir contributor guide! This comprehensive guide covers everything you need to know about contributing to the project.
Quick Links
Section titled “Quick Links”- Development Setup - Detailed development environment setup
- Architecture - Technical architecture overview
- GitHub Repository
- Issue Tracker
Table of Contents
Section titled “Table of Contents”- Getting Started
- Development Workflow
- Code Style and Standards
- Testing Guidelines
- Pull Request Process
- Architecture Overview
- Design Principles
- CI and Releases
- Release Process
Getting Started
Section titled “Getting Started”First-Time Contributors
Section titled “First-Time Contributors”Welcome! Here’s how to make your first contribution:
- Find an issue - Look for issues labeled
good first issueorhelp wanted - Comment on the issue - Let us know you’re working on it
- Fork and clone - Create your own fork and clone it locally
- Set up your environment - Follow the Development Setup guide
- Make your changes - Implement your fix or feature
- Submit a PR - Create a pull request with your changes
Finding Work
Section titled “Finding Work”- Good First Issues - Tagged issues suitable for newcomers
- Help Wanted - Issues where we’d appreciate community help
- Bug Reports - Always welcome to fix reported bugs
- Feature Requests - Check if a feature request needs implementation
- Documentation - Help improve our docs
Development Workflow
Section titled “Development Workflow”Branch Strategy
Section titled “Branch Strategy”main- Stable release branch- Feature branches -
feature/descriptionorfix/description - Always branch from
main
git checkout maingit pull origin maingit checkout -b feature/my-new-featureMaking Changes
Section titled “Making Changes”- Write code following our style guidelines
- Test your changes locally
- Commit frequently with clear messages
- Push to your fork regularly
- Keep your branch updated with main
# Keep your branch up to dategit fetch origingit rebase origin/main
# Or merge if you prefergit merge origin/mainBefore Submitting
Section titled “Before Submitting”Checklist before creating a pull request:
- All tests pass locally
- Code follows style guidelines
- New tests added for new functionality
- Documentation updated if needed
- Commit messages follow conventions
- No merge conflicts with main
- Changes are focused and minimal
Code Style and Standards
Section titled “Code Style and Standards”Rust Code Style
Section titled “Rust Code Style”Formatting:
# Format all Rust codecargo fmt
# Check formatting without changingcargo fmt -- --checkLinting:
# Run clippycargo clippy --all-targets --all-features
# Fix auto-fixable warningscargo clippy --fixBest Practices:
- Use meaningful variable and function names
- Write doc comments for public APIs
- Keep functions focused and reasonably sized
- Prefer explicit error handling over unwrap()
- Use
Result<T, E>for operations that can fail - Follow the Rust API Guidelines
Example:
Campaign IDs are TEXT UUIDs (String), not integers:
/// Retrieves a campaign by ID from the database.////// # Arguments/// * `conn` - Database connection/// * `id` - Campaign ID (UUID string) to retrieve////// # Returns/// * `Ok(Campaign)` - The campaign if found/// * `Err(String)` - Error message if not foundpub fn get_campaign(conn: &mut SqliteConnection, id: &str) -> Result<Campaign, String> { campaigns::table .find(id) .first(conn) .map_err(|e| format!("Failed to get campaign: {}", e))}TypeScript/Vue Code Style
Section titled “TypeScript/Vue Code Style”Type Safety:
- Always use TypeScript, not JavaScript
- Define interfaces for all data structures
- Avoid
anytype unless absolutely necessary - Use strict mode in tsconfig.json
Vue Best Practices:
- Use Composition API with
<script setup> - Keep components focused and single-purpose
- Props should have types and validators
- Emit events with proper typing
- Use composables for reusable logic
Example:
<script setup lang="ts">import { ref, computed } from 'vue';import type { Campaign } from '@/types/campaigns';
interface Props { campaign: Campaign; editable?: boolean;}
const props = withDefaults(defineProps<Props>(), { editable: false});
const emit = defineEmits<{ save: [campaign: Campaign]; cancel: [];}>();
const localCampaign = ref({ ...props.campaign });
const hasChanges = computed(() => { return JSON.stringify(localCampaign.value) !== JSON.stringify(props.campaign);});
function handleSave() { emit('save', localCampaign.value);}</script>General Conventions
Section titled “General Conventions”Naming:
- Rust:
snake_casefor functions/variables,PascalCasefor types - TypeScript:
camelCasefor variables/functions,PascalCasefor classes/interfaces - Files: Match the primary export name
Comments:
- Write self-documenting code
- Add comments for complex logic
- Doc comments for public APIs
- No commented-out code in commits
Prohibited:
- No emojis in code, comments, or commit messages
- No references to Claude or Anthropic in commit messages
- No hardcoded secrets or credentials
- No console.log in production code (use logging frameworks)
Testing Guidelines
Section titled “Testing Guidelines”Rust Testing
Section titled “Rust Testing”Unit Tests:
New campaigns are built with NewCampaign::new(id, name) — the ID is a caller-supplied UUID string:
#[cfg(test)]mod tests { use super::*;
#[test] fn test_campaign_creation() { let campaign = NewCampaign::new("test-id", "Test Campaign") .with_description("A test campaign"); assert_eq!(campaign.id, "test-id"); assert_eq!(campaign.name, "Test Campaign"); }}Integration Tests:
Place in the crate’s tests/ directory. Existing targets: catalog_import and srd_smoke_test in mimir-core; cli_integration, format_roundtrip, and polygon_snapshots in mimir-mapgen.
use mimir_core::{create_connection, run_migrations};
#[test]fn test_database_integration() { let mut conn = create_connection(":memory:").unwrap(); run_migrations(&mut conn).unwrap(); // Test database operations}Frontend Testing
Section titled “Frontend Testing”Component Tests:
import { describe, it, expect } from 'vitest';import { mount } from '@vue/test-utils';import CampaignCard from '@/components/CampaignCard.vue';
describe('CampaignCard', () => { it('renders campaign name', () => { const wrapper = mount(CampaignCard, { props: { campaign: { id: 1, name: 'Test Campaign' } } }); expect(wrapper.text()).toContain('Test Campaign'); });});Running Tests
Section titled “Running Tests”# Rust tests (recommended)angreal test unit# Underlying command:# cargo test --workspace --exclude mimir -- --test-threads=1# (excludes the Tauri crate, which needs the sidecar binary to build;# --test-threads=1 avoids SQLite locking issues)
# Frontend testscd crates/mimir/frontend && npm test
# With coverageangreal test coverage # Rust (cargo-tarpaulin)npm run test:coverage # Frontend
# Specific integration test targetcargo test -p mimir-core --test catalog_import -- --test-threads=1
# Specific test by namecargo test -p mimir-core test_campaign_creation -- --test-threads=1Pull Request Process
Section titled “Pull Request Process”1. Prepare Your PR
Section titled “1. Prepare Your PR”- Ensure all tests pass
- Update documentation
- Write a clear PR description
- Reference related issues
2. PR Description Template
Section titled “2. PR Description Template”## DescriptionBrief description of what this PR does
## Related IssuesFixes #123Related to #456
## Changes Made- Added feature X- Fixed bug Y- Updated documentation for Z
## Testing- [ ] Unit tests added/updated- [ ] Integration tests added/updated- [ ] Manually tested on macOS/Windows/Linux
## Checklist- [ ] Code follows style guidelines- [ ] Self-review completed- [ ] Documentation updated- [ ] No new warnings- [ ] Tests pass locally3. Review Process
Section titled “3. Review Process”- Maintainers will review your PR
- Address feedback and requested changes
- Keep discussion focused and professional
- Be patient - reviews may take a few days
4. After Approval
Section titled “4. After Approval”- PR will be merged to main
- Your contribution will be included in the next release
- Thank you for contributing!
Architecture Overview
Section titled “Architecture Overview”System Architecture
Section titled “System Architecture”Mimir follows a clean architecture pattern with clear separation of concerns:
┌─────────────────────────────────────────┐│ Frontend (Vue 3 + TS) ││ ┌──────────────────────────────────┐ ││ │ Components, Views, Stores │ ││ └──────────────────────────────────┘ │└─────────────────┬───────────────────────┘ │ Tauri IPC┌─────────────────▼───────────────────────┐│ Tauri Desktop Shell (Rust) ││ ┌──────────────────────────────────┐ ││ │ Commands, Services, State │ ││ └──────────────┬───────────────────┘ │└─────────────────┼───────────────────────┘ │┌─────────────────▼───────────────────────┐│ mimir-core (Business Logic) ││ ┌──────────────────────────────────┐ ││ │ Services, DAL, Domain Models │ ││ └──────────────┬───────────────────┘ │└─────────────────┼───────────────────────┘ │┌─────────────────▼───────────────────────┐│ Database (SQLite + Diesel) │└─────────────────────────────────────────┘Key Crates
Section titled “Key Crates”- mimir - Tauri app shell and command handlers
- mimir-core - Business logic, domain models, database
- mimir-mcp - MCP server for Claude Code integration
- mimir-mapgen - Procedural map generation (library + CLI)
- mimir-print - PDF generation via Typst
Design Principles
Section titled “Design Principles”- Local-First - All data stored locally, no cloud dependencies
- Privacy-First - No telemetry, no tracking, user data stays local
- Type Safety - Strong typing in both Rust and TypeScript
- Separation of Concerns - Clear boundaries between layers
- Testability - Write testable code with good test coverage
- Domain-Driven Design - Model the D&D campaign management domain
- Progressive Enhancement - Core features work without LLM
- Cross-Platform - Native experience on Windows, macOS, Linux
CI and Releases
Section titled “CI and Releases”Continuous Integration
Section titled “Continuous Integration”CI (.github/workflows/ci.yml) runs on pushes and pull requests to main:
- Build matrix: macOS (aarch64 and x86_64), Ubuntu 22.04, and Windows — each builds the MCP sidecar then the full Tauri app
- Frontend tests: vitest on Ubuntu
- Core crate tests:
angreal test unit --coreon Ubuntu - SRD smoke test (heavy integration,
cargo test -p mimir-core --test srd_smoke_test): runs only on pushes tomain, not on pull requests - Coverage: cargo-tarpaulin on
mimir-core, run with explicit flags andcontinue-on-error: true, so CI reports coverage but does not fail on it. The 50% floor (fail-under = 50intarpaulin.toml) is enforced only by the localangreal test coveragetask
Releases
Section titled “Releases”Releases (.github/workflows/release.yml) are triggered by pushing a tag matching v*.*.*:
- The same four-platform matrix builds the sidecar, the
mimir-mapgenbinary, and the Tauri app bundles tauri-actioncreates a draft GitHub release taggedapp-v<version>- A follow-up job attaches the
mimir-mcpsidecar andmimir-mapgenbinaries for every target to that release
Documentation Deploys
Section titled “Documentation Deploys”Pull requests that touch docs/** run .github/workflows/docs.yml, which builds the Starlight site. Pushes to main that touch docs/** also deploy it to the external repository mimir-dm/mimir-dm.github.io (GitHub Pages).
Release Process
Section titled “Release Process”Version Numbering
Section titled “Version Numbering”We follow Semantic Versioning:
- MAJOR.MINOR.PATCH (e.g., 0.1.0)
- MAJOR: Breaking changes
- MINOR: New features, backwards compatible
- PATCH: Bug fixes, backwards compatible
Release Steps
Section titled “Release Steps”- Update version in all
Cargo.tomlfiles - Update version in
package.json - Update version in
tauri.conf.json - Create git tag:
git tag vX.Y.Z - Push tag:
git push origin vX.Y.Z - GitHub Actions builds the matrix and creates a draft release
app-vX.Y.Zwith sidecar and mapgen binaries attached
Questions and Help
Section titled “Questions and Help”Getting Help
Section titled “Getting Help”- Documentation - Check this guide and DEVELOPMENT.md
- Issues - Search existing issues or create a new one
- Discussions - Use GitHub Discussions for questions
- Pull Requests - Ask questions in PR comments
Maintainer Response Times
Section titled “Maintainer Response Times”- Issues: Usually within 1-3 days
- PRs: Usually within 3-7 days
- Critical bugs: Within 24 hours when possible
Recognition
Section titled “Recognition”Contributors are recognized in:
- Git commit history
- Release notes
- Project documentation
- GitHub contributors page
Thank you for contributing to Mimir!