Contributing

Thank you for your interest in contributing to NornWeave! This document provides guidelines and information for contributors.

Table of Contents

Code of Conduct

By participating in this project, you agree to abide by our Code of Conduct. Please read it before contributing.

Getting Started

Prerequisites

  • Python 3.14 or higher
  • uv (recommended) or pip
  • Docker and Docker Compose (for local development)
  • Git

Finding Issues

  • Look for issues labeled good first issue for beginner-friendly tasks
  • Issues labeled help wanted are looking for contributors
  • Feel free to ask questions on any issue before starting work

Development Setup

  1. Fork and clone the repository

    git clone https://github.com/YOUR_USERNAME/nornweave.git
    cd nornweave
  2. Install dependencies

    # Install uv if you don't have it: https://docs.astral.sh/uv/
    make install-dev
  3. Set up pre-commit hooks

    make setup
  4. Start the development stack

    # Start PostgreSQL and Redis
    docker compose up -d postgres redis
    
    # Copy environment file
    cp .env.example .env
    
    # Run migrations
    make migrate
    
    # Start the development server
    make dev
  5. Verify your setup

    # Run tests
    make test
    
    # Run linter
    make lint

Making Changes

Branch Naming

Use descriptive branch names following this pattern:

  • feature/description - New features
  • fix/description - Bug fixes
  • docs/description - Documentation changes
  • refactor/description - Code refactoring
  • test/description - Test additions or fixes

Commit Messages

We follow Conventional Commits. Format:

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting, etc.)
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: Maintenance tasks

Examples:

feat(yggdrasil): add rate limiting middleware
fix(verdandi): handle malformed HTML in email parsing
docs(readme): update installation instructions

Coding Standards

Python Style

  • Follow PEP 8 guidelines
  • Use type hints for all functions and methods
  • Maximum line length: 100 characters
  • Use ruff for linting and formatting

Code Quality Checks

Before submitting, ensure all checks pass:

# Run all checks
make check

# Individual checks
make lint        # Linting
make format      # Formatting
make typecheck   # Type checking

Architecture Guidelines

NornWeave uses a modular architecture with clear boundaries:

ModuleResponsibility
core/Shared interfaces, config, exceptions
models/Pydantic and SQLAlchemy models
urdr/Storage adapters (PostgreSQL, SQLite)
verdandi/Email parsing and processing
skuld/Outbound sending and rate limiting
yggdrasil/FastAPI routes and middleware
adapters/Email provider adapters
huginn/MCP read operations
muninn/MCP write operations

Key Principles:

  • Depend on abstractions, not implementations
  • Each module should have a clear, single responsibility
  • Use dependency injection for testability
  • Keep business logic separate from infrastructure

Testing

Running Tests

# All tests
make test

# Unit tests only
make test-unit

# Integration tests only
make test-integration

# With coverage
make test-cov

Writing Tests

  • Place unit tests in tests/unit/
  • Place integration tests in tests/integration/
  • Use fixtures from tests/conftest.py
  • Aim for high coverage on business logic

Test Structure:

import pytest
from nornweave.verdandi.parser import parse_email_content


class TestParseEmailContent:
    """Tests for email content parsing."""

    def test_converts_html_to_markdown(self) -> None:
        """HTML should be converted to clean Markdown."""
        html = "<p>Hello <strong>World</strong></p>"
        result = parse_email_content(html)
        assert result == "Hello **World**"

    def test_removes_reply_cruft(self) -> None:
        """Reply quotes should be stripped."""
        content = "Thanks!\n\nOn Jan 1, 2024, John wrote:\n> Original message"
        result = parse_email_content(content)
        assert "On Jan 1" not in result

Documentation

Docstrings

Use Google-style docstrings:

def send_email(
    to: list[str],
    subject: str,
    body: str,
    *,
    thread_id: str | None = None,
) -> str:
    """Send an email through the configured provider.

    Args:
        to: List of recipient email addresses.
        subject: Email subject line.
        body: Email body in Markdown format.
        thread_id: Optional thread ID for replies.

    Returns:
        The provider's message ID.

    Raises:
        EmailProviderError: If sending fails.
        ValidationError: If recipients are invalid.
    """

Updating Documentation

Documentation is in the docs/ directory using MkDocs:

# Serve locally
make docs

# Build
make docs-build

Submitting Changes

Pull Request Process

  1. Create a PR with a clear title and description
  2. Link related issues using keywords (e.g., “Fixes #123”)
  3. Ensure CI passes - all checks must be green
  4. Request review from maintainers
  5. Address feedback and update as needed
  6. Squash and merge once approved

PR Description Template

## Summary
Brief description of changes.

## Changes
- Change 1
- Change 2

## Testing
How were these changes tested?

## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] CHANGELOG updated (for user-facing changes)

Release Process

Releases are managed by maintainers following Semantic Versioning:

  • MAJOR: Breaking API changes
  • MINOR: New features, backward compatible
  • PATCH: Bug fixes, backward compatible

Main NornWeave Release

The main NornWeave server is published to both GitHub Releases and PyPI.

Prerequisites (one-time setup):

  • PyPI project nornweave exists
  • Trusted publisher configured on PyPI for this repo (OIDC)
  • GitHub environment pypi-publish configured

Release steps:

# 1. Update version in pyproject.toml
# Edit: version = "X.Y.Z"

# 2. Update CHANGELOG.md
# - Move items from [Unreleased] to new version section
# - Add release date: ## [X.Y.Z] - YYYY-MM-DD

# 3. Commit the changes
git add pyproject.toml CHANGELOG.md
git commit -m "chore: release vX.Y.Z"

# 4. Create annotated tag (must match pyproject.toml version)
git tag -a vX.Y.Z -m "Release vX.Y.Z"

# 5. Push commit and tag (triggers PyPI publish)
git push origin main --tags

The GitHub Actions workflow (.github/workflows/server-pypi-publish.yml) will:

  • Build source distribution and wheel using hatch build
  • Publish to PyPI using trusted publishing (OIDC, no token needed)

The GitHub release is created manually:

  1. Go to Releases
  2. Click “Draft a new release”
  3. Select the tag vX.Y.Z
  4. Use “Generate release notes” or copy from CHANGELOG.md
  5. Publish the release

Important: The vX.Y.Z tag must match the version in pyproject.toml. For example, tag v0.1.1 requires version = "0.1.1" in pyproject.toml.

n8n Node Release (@nornweave/n8n-nodes-nornweave)

The n8n community node is published to npm with provenance attestation.

Release steps:

cd packages/n8n-nodes-nornweave

# 1. Bump version
npm version patch  # or minor/major

# 2. Commit the version bump
git add package.json package-lock.json
git commit -m "chore(n8n): release vX.Y.Z"

# 3. Create and push tag (triggers CI publish)
git tag n8n-vX.Y.Z
git push origin main --tags

The GitHub Actions workflow (.github/workflows/n8n-node.yml) will:

  • Build and test the package
  • Publish to npm with provenance (OIDC, no token needed)
  • Create a GitHub release

Python Client Release (nornweave-client)

The Python client is published to PyPI using trusted publishing (OIDC).

Prerequisites (one-time setup):

  • PyPI project nornweave-client exists
  • Trusted publisher configured on PyPI for this repo
  • GitHub environment pypi-publish configured

Release steps:

cd clients/python

# 1. Update version in pyproject.toml
# Edit: version = "X.Y.Z"

# 2. Update CHANGELOG if needed

# 3. Commit the version bump
git add pyproject.toml
git commit -m "chore(python-client): release vX.Y.Z"

# 4. Create and push tag (triggers CI publish)
git tag python-vX.Y.Z
git push origin main --tags

The GitHub Actions workflow (.github/workflows/client-pypi-publish.yml) will:

  • Run tests and linting
  • Build source distribution and wheel
  • Publish to PyPI with OIDC (no token needed)

First-time publish (local):

For the initial publish before OIDC is configured:

cd clients/python

# Build the package
uv build
# or: python -m build

# Upload to PyPI (requires API token)
uv publish
# or: twine upload dist/*

Then configure trusted publishing on PyPI by adding the GitHub repository as a trusted publisher.

Version Alignment

The packages are versioned independently:

PackageVersion LocationTag FormatRegistry
NornWeave Serverpyproject.tomlvX.Y.ZPyPI (nornweave)
n8n Nodepackages/n8n-nodes-nornweave/package.jsonn8n-vX.Y.Znpm
Python Clientclients/python/pyproject.tomlpython-vX.Y.ZPyPI (nornweave-client)

Important:

  • The vX.Y.Z tag on the monorepo matches the main nornweave PyPI package version
  • When making breaking API changes to the server, coordinate releases across all packages to ensure compatibility

MCP Registry Submission

NornWeave is listed on several MCP registries to help users discover the MCP server. When releasing a new version, update the registry listings:

Registry Metadata Files

The repository includes metadata files for each registry:

FileRegistryDocumentation
smithery.yamlSmithery.aiDocs
mcp-get.jsonmcp-get.comDocs
glama.jsonGlama.aiDocs

Updating Registry Listings

  1. Update metadata files - When adding new tools, resources, or changing capabilities, update the corresponding metadata files in the repository root.

  2. Version bump - Update the version field in all metadata files to match the new release version.

  3. Submit updates - Most registries automatically index from GitHub. For manual submission:

  4. Verify listings - After release, verify the server appears correctly on each registry.

Questions?

Thank you for contributing to NornWeave!