# Contributing to Headroom

Thank you for your interest in contributing to Headroom! This document provides guidelines and instructions for contributing.

## Code of Conduct

By participating in this project, you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md).

## How to Contribute

### Reporting Bugs

Before creating a bug report, please check existing issues to avoid duplicates. When creating a bug report, include:

- **Clear title** describing the issue
- **Steps to reproduce** the behavior
- **Expected behavior** vs what actually happened
- **Environment details** (Python version, OS, Headroom version)
- **Code samples** or minimal reproduction if possible

### Suggesting Features

Feature requests are welcome! Please:

- Check existing issues/discussions first
- Clearly describe the use case and motivation
- Explain how it fits with Headroom's goals (context optimization, safety, determinism)

### Pull Requests

1. **Fork the repository** and create your branch from `main`
2. **Install development dependencies**:
   ```bash
   pip install -e ".[dev]"
   ```
3. **Make your changes** following our coding standards
4. **Add tests** for new functionality
5. **Run the test suite**:
   ```bash
   pytest
   ```
6. **Run linting**:
   ```bash
   ruff check .
   ruff format .
   ```
7. **Update documentation** if needed
8. **Submit your PR** with a clear description

## Development Setup

```bash
# Clone the repository
git clone https://github.com/chopratejas/headroom.git
cd headroom

# Create a virtual environment
python -m venv .venv
source .venv/bin/activate  # or `.venv\Scripts\activate` on Windows

# Install in development mode with all dependencies
pip install -e ".[dev,relevance,proxy]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=headroom --cov-report=html
```

### Dev Containers

If you use VS Code or GitHub Codespaces, Headroom ships two Dev Container configs:

- **`.devcontainer/devcontainer.json`** - default contributor environment with Python 3.12, `uv`, Node.js, and GitHub CLI
- **`.devcontainer/memory-stack/devcontainer.json`** - the same environment plus Qdrant and Neo4j sidecars, with the locked `memory-stack` extra installed for `qdrant-neo4j` backend work

Inside the memory-stack container, use the sidecar service names instead of `localhost`:

```bash
qdrant:6333
neo4j://neo4j:7687
```

The default container runs `uv sync --frozen --extra dev` on creation, so the usual repo commands become:

```bash
uv run ruff check .
uv run ruff format --check .
uv run mypy headroom --ignore-missing-imports
uv run pytest -v --tb=short
```

## Coding Standards

### Style

- We use [Ruff](https://github.com/astral-sh/ruff) for linting and formatting
- Line length: 100 characters
- Use type hints for all public functions
- Follow PEP 8 naming conventions

### Code Organization

```
headroom/
├── __init__.py          # Public API exports
├── client.py            # HeadroomClient wrapper
├── config.py            # Configuration dataclasses
├── transforms/          # Context transforms
│   ├── smart_crusher.py # Statistical compression
│   ├── cache_aligner.py # Cache optimization
│   └── rolling_window.py# Context windowing
├── relevance/           # Relevance scoring
├── providers/           # LLM provider adapters
├── proxy/               # Proxy server
└── storage/             # Metrics storage
```

### Testing

- Write tests for all new functionality
- Use pytest fixtures for common setup
- Test edge cases and error conditions
- Aim for >80% coverage on new code

Example test structure:
```python
class TestSmartCrusher:
    """Tests for SmartCrusher transform."""

    def test_compresses_large_arrays(self):
        """Should compress arrays above token threshold."""
        ...

    def test_preserves_errors(self):
        """Should never drop items containing errors."""
        ...
```

### Documentation

- Add docstrings to all public classes and functions
- Use Google-style docstrings
- Update README.md for user-facing changes
- Add examples for new features

```python
def compress_tool_output(
    content: str,
    max_items: int = 50,
) -> str:
    """Compress tool output while preserving important items.

    Args:
        content: The tool output content (usually JSON).
        max_items: Maximum items to keep in arrays.

    Returns:
        Compressed content string.

    Raises:
        ValueError: If content is not valid JSON.

    Example:
        >>> compress_tool_output('[{"id": 1}, {"id": 2}]', max_items=1)
        '[{"id": 1}]'
    """
```

## Pull Request Guidelines

### PR Title Format

Use conventional commit style:
- `feat: Add semantic caching to proxy`
- `fix: Handle empty tool outputs correctly`
- `docs: Update proxy documentation`
- `test: Add tests for CacheAligner`
- `refactor: Simplify rolling window logic`

### PR Description

Include:
- **What** changes were made
- **Why** the changes were needed
- **How** to test the changes
- **Breaking changes** if any

### Review Process

1. All PRs require at least one review
2. CI must pass (tests, linting, type checking)
3. Maintain or improve test coverage
4. Update CHANGELOG.md for notable changes

## Architecture Decisions

### Safety First

Headroom's core principle is **safety**. When in doubt:
- Never drop user/assistant content
- Never break tool call/response pairing
- Malformed content passes through unchanged
- Prefer false negatives over false positives

### Performance

- Transforms should add <50ms latency at P99
- Use lazy loading for optional dependencies
- Profile before optimizing

### Compatibility

- Support Python 3.10+
- Core functionality has minimal dependencies
- Optional features use extras (e.g., `pip install headroom[relevance]`)


## Recognition

Contributors are recognized in:
- The CHANGELOG for their contributions
- The GitHub contributors page
- Release notes for significant features

Thank you for contributing to Headroom!
