diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..aa43844
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,65 @@
+name: Deploy Docs
+
+on:
+ push:
+ branches: [main]
+ paths: ['docs/**', '.github/workflows/docs.yml']
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 9.15.4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+ cache: 'pnpm'
+ cache-dependency-path: 'docs/pnpm-lock.yaml'
+
+ - name: Install dependencies
+ working-directory: docs
+ run: pnpm install --frozen-lockfile
+
+ - name: Build
+ working-directory: docs
+ env:
+ BASE_PATH: /eventkit
+ NODE_ENV: production
+ run: pnpm build
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v5
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v4
+ with:
+ path: './docs/out'
+
+ deploy:
+ runs-on: ubuntu-latest
+ needs: build
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/README.md b/README.md
index 9efc9b6..d2f9d10 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,8 @@ Event ingestion and processing kit for Python.
**Philosophy**: Provide a solid starting point with battle-tested patterns, then get out of your way. Customize for your specific needs.
+**📚 [View Full Documentation](https://prosdevlab.github.io/eventkit/)**
+
### Key Features
- **Flexible ingestion** - Accept any JSON payload with Segment-compatible API
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..d1b55a9
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,36 @@
+# Dependencies
+node_modules/
+.pnp
+.pnp.js
+
+# Testing
+coverage/
+
+# Next.js
+.next/
+out/
+dist/
+
+# Production
+build/
+
+# Misc
+.DS_Store
+*.pem
+
+# Debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Local env files
+.env*.local
+.env
+
+# Vercel
+.vercel
+
+# TypeScript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..feec972
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,54 @@
+# eventkit Documentation Site
+
+This directory contains the Nextra-based documentation site for eventkit.
+
+## Development
+
+```bash
+# Install dependencies
+pnpm install
+
+# Start dev server
+pnpm dev
+
+# Build for production
+pnpm build
+```
+
+The dev server will start at http://localhost:3000
+
+## Technology Stack
+
+- **Nextra v4.0.0** - Documentation framework
+- **Next.js 15.1.6** - React framework
+- **React 19** - UI library
+- **pnpm** - Package manager
+
+## Project Structure
+
+```
+docs/
+├── app/ # Next.js app directory
+│ ├── layout.tsx # Root layout with navigation
+│ └── [[...mdxPath]]/ # Dynamic MDX page routing
+├── content/ # MDX documentation files
+│ ├── _meta.json # Navigation structure
+│ └── *.mdx # Documentation pages
+├── public/ # Static assets
+├── next.config.mjs # Next.js configuration
+├── tsconfig.json # TypeScript configuration
+└── package.json # Dependencies and scripts
+```
+
+## Deployment
+
+The site is automatically deployed to GitHub Pages at https://prosdevlab.github.io/eventkit/ when changes are pushed to the `main` branch.
+
+## Adding Content
+
+1. Create an MDX file in `content/`
+2. Add navigation entry to `content/_meta.json`
+3. Write content using MDX syntax
+4. Test locally with `pnpm dev`
+
+See [Nextra documentation](https://nextra.site/) for more details on MDX features and components.
diff --git a/docs/app/[[...mdxPath]]/page.tsx b/docs/app/[[...mdxPath]]/page.tsx
new file mode 100644
index 0000000..ae2c92f
--- /dev/null
+++ b/docs/app/[[...mdxPath]]/page.tsx
@@ -0,0 +1,23 @@
+import { generateStaticParamsFor, importPage } from 'nextra/pages';
+import { useMDXComponents } from 'nextra-theme-docs';
+
+export const generateStaticParams = generateStaticParamsFor('mdxPath');
+
+export async function generateMetadata(props: { params: Promise<{ mdxPath?: string[] }> }) {
+ const params = await props.params;
+ const { metadata } = await importPage(params.mdxPath);
+ return metadata;
+}
+
+export default async function Page(props: { params: Promise<{ mdxPath?: string[] }> }) {
+ const params = await props.params;
+ const result = await importPage(params.mdxPath);
+ const { default: MDXContent, ...rest } = result;
+ const { wrapper: Wrapper } = useMDXComponents();
+
+ return (
+
+
+
+ );
+}
diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx
new file mode 100644
index 0000000..50b47cb
--- /dev/null
+++ b/docs/app/layout.tsx
@@ -0,0 +1,55 @@
+import { Banner, Head } from 'nextra/components';
+import { getPageMap } from 'nextra/page-map';
+import { Footer, Layout, Navbar } from 'nextra-theme-docs';
+import 'nextra-theme-docs/style.css';
+
+export const metadata = {
+ title: 'eventkit - Event Ingestion and Processing Kit for Python',
+ description:
+ 'A production-ready kit for building event collection pipelines with flexible ingestion, stream-based routing, and pluggable storage',
+};
+
+const banner = (
+
+ eventkit is in active development. APIs may change.
+
+);
+
+const navbar = (
+ eventkit}
+ projectLink="https://github.com/prosdevlab/eventkit"
+ chatLink="https://github.com/prosdevlab/eventkit/discussions"
+ />
+);
+
+const footer = (
+
+);
+
+export default async function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/docs/content/advanced/index.mdx b/docs/content/advanced/index.mdx
new file mode 100644
index 0000000..f0e2b78
--- /dev/null
+++ b/docs/content/advanced/index.mdx
@@ -0,0 +1,168 @@
+---
+title: Advanced
+description: Advanced patterns and production deployment guides
+---
+
+# Advanced
+
+Advanced patterns, production deployment, and performance tuning for eventkit.
+
+## Topics
+
+### Architecture Patterns
+
+- **Dual-Path Architecture** - Process events through multiple coordinators (coming soon)
+- **Performance Tuning** - Optimize for throughput and latency (coming soon)
+- **Monitoring & Observability** - Prometheus metrics and logging (coming soon)
+
+### Production Deployment
+
+- **Cloud Run** - Serverless deployment (coming soon)
+- **GKE/Kubernetes** - Container orchestration (coming soon)
+- **Horizontal Scaling** - Multi-instance patterns (coming soon)
+
+### Performance
+
+eventkit is designed for high throughput with low latency:
+
+**Throughput:**
+- 10,000+ events/second validated (single instance)
+- Horizontal scaling with PubSubQueue mode
+- Adaptive batching reduces storage writes
+
+**Latency:**
+- p50: less than 25ms (collection endpoint)
+- p95: less than 100ms (target)
+- Ring buffer adds less than 1ms overhead
+
+**Benchmarks:**
+```bash
+pytest tests/performance/ --benchmark-only
+```
+
+See `specs/performance-benchmarks/` for detailed results.
+
+### Ring Buffer (Write-Ahead Log)
+
+eventkit uses a ring buffer for durability - events are never lost even if the service crashes.
+
+**Architecture:**
+```mermaid
+%%{init: {'theme':'neutral', 'themeVariables': {'lineColor':'#888','edgeLabelBackground':'transparent'}}}%%
+graph LR
+ API[API] --> RB[(Ring Buffer
SQLite WAL)]
+ RB --> Pub[Publisher]
+ Pub --> Queue[Queue]
+ Queue --> Workers[Workers]
+ Workers --> Store[(Storage)]
+
+ classDef storage stroke-width:3px
+ class RB,Store storage
+```
+
+**Why SQLite?**
+- Local durability (no network on hot path)
+- WAL mode for concurrent reads/writes
+- Zero dependencies
+- Production-proven pattern
+
+**Configuration:**
+```bash
+export EVENTKIT_RING_BUFFER_DB_PATH="./eventkit_ring_buffer.db"
+export EVENTKIT_RING_BUFFER_MAX_SIZE="100000"
+export EVENTKIT_RING_BUFFER_RETENTION_HOURS="24"
+```
+
+### Monitoring
+
+eventkit exposes Prometheus metrics on port 9090:
+
+```bash
+curl http://localhost:9090/metrics
+```
+
+**Key Metrics:**
+- Event throughput (events/sec)
+- Queue depth and processing lag
+- Storage write latency
+- Error rates
+
+**Grafana Dashboards:**
+```promql
+# Event processing rate
+rate(eventkit_events_processed_total[5m])
+
+# Queue backlog
+eventkit_queue_depth
+
+# Error rate
+rate(eventkit_events_failed_total[5m]) / rate(eventkit_events_received_total[5m])
+```
+
+### Custom Adapters
+
+Implement custom validation and transformation logic:
+
+```python
+from eventkit.adapters.base import SchemaAdapter, AdapterResult
+from eventkit.schema.raw import RawEvent
+from eventkit.schema.events import TypedEvent
+
+class MyCustomAdapter(SchemaAdapter):
+ def adapt(self, raw: RawEvent) -> AdapterResult[TypedEvent]:
+ # Custom validation
+ if not self._is_valid(raw.payload):
+ return AdapterResult.failure("Invalid format")
+
+ # Custom transformation
+ event = self._transform(raw.payload)
+ return AdapterResult.success(event)
+
+ def _is_valid(self, payload: dict) -> bool:
+ # Your validation logic
+ return True
+
+ def _transform(self, payload: dict) -> TypedEvent:
+ # Your transformation logic
+ return TypedEvent(...)
+```
+
+### Error Handling
+
+eventkit never drops events - validation failures go to the error store:
+
+```python
+# Invalid events are stored for debugging
+errors = await error_store.query_errors(limit=100)
+for error in errors:
+ print(f"Error: {error['error']}")
+ print(f"Payload: {error['payload']}")
+ print(f"Timestamp: {error['timestamp']}")
+```
+
+**Common Error Patterns:**
+- Missing required fields
+- Type mismatches
+- Invalid timestamps
+- Unknown event types
+
+### Security
+
+**Authentication & Authorization:**
+eventkit doesn't provide built-in auth. Use a reverse proxy (Cloud Run, API Gateway) for authentication.
+
+**PII Handling:**
+- Never log full event payloads
+- Use structured logging with allowlists
+- Consider field-level encryption for sensitive data
+
+**Network Security:**
+- Run behind load balancer (TLS termination)
+- Use VPC for internal services
+- Enable Cloud Armor for DDoS protection
+
+## Next Steps
+
+- **[Architecture](/core-concepts/architecture)** - Deep dive into system design
+- **[Configuration](/api/configuration)** - Environment variables reference
+- **[Contributing](/contributing)** - Contribute to eventkit
diff --git a/docs/content/api/index.mdx b/docs/content/api/index.mdx
new file mode 100644
index 0000000..fd13ea0
--- /dev/null
+++ b/docs/content/api/index.mdx
@@ -0,0 +1,267 @@
+---
+title: API Reference
+description: Complete API reference for eventkit
+---
+
+# API Reference
+
+Complete reference for eventkit's HTTP API, configuration, and protocols.
+
+## HTTP API
+
+### Health Endpoints
+
+```bash
+GET /health
+```
+
+**Liveness probe** - Returns 200 if process is running (no dependencies checked).
+
+```json
+{"status": "ok"}
+```
+
+```bash
+GET /ready
+```
+
+**Readiness probe** - Returns 200 if dependencies (Firestore, GCS) are healthy, 503 if not.
+
+```json
+{"status": "ready"}
+```
+
+### Collection Endpoints
+
+```bash
+POST /collect/{stream}
+POST /collect
+```
+
+**Flexible event collection** - Accept any JSON payload.
+
+**Parameters:**
+- `stream` (path, optional): Stream name for routing (default: "default")
+
+**Request Body:**
+- Single event (object)
+- Batch of events (array)
+
+**Response:** 202 Accepted
+```json
+{"status": "accepted"}
+```
+
+**Example:**
+```bash
+curl -X POST http://localhost:8000/collect/users \
+ -H "Content-Type: application/json" \
+ -d '{"type": "identify", "userId": "user_123", "traits": {"email": "user@example.com"}}'
+```
+
+### Segment-Compatible Endpoints
+
+```bash
+POST /v1/identify
+```
+
+**Identify users** - Create or update user profiles.
+
+**Request Body:**
+```json
+{
+ "type": "identify",
+ "userId": "user_123",
+ "traits": {
+ "email": "user@example.com",
+ "name": "John Doe",
+ "plan": "enterprise"
+ }
+}
+```
+
+Routes to `users` stream.
+
+```bash
+POST /v1/track
+```
+
+**Track events** - Record user actions.
+
+**Request Body:**
+```json
+{
+ "type": "track",
+ "userId": "user_123",
+ "event": "Button Clicked",
+ "properties": {
+ "button_id": "cta-signup",
+ "page": "/home"
+ }
+}
+```
+
+Routes to `events` stream.
+
+```bash
+POST /v1/page
+```
+
+**Track page views** - Record page navigation.
+
+**Request Body:**
+```json
+{
+ "type": "page",
+ "userId": "user_123",
+ "name": "Home Page",
+ "properties": {
+ "url": "/home",
+ "referrer": "/landing"
+ }
+}
+```
+
+Routes to `pages` stream.
+
+## Configuration
+
+See **[Configuration Reference](/api/configuration)** for all environment variables.
+
+**Key Settings:**
+
+| Category | Variable | Default | Description |
+|----------|----------|---------|-------------|
+| **Storage** | `EVENTKIT_EVENT_STORE` | `"gcs"` | Storage backend (`gcs`, `firestore`) |
+| | `GCP_GCS_BUCKET` | *required* | GCS bucket name |
+| | `GCP_BIGQUERY_DATASET` | *required* | BigQuery dataset |
+| **Queue** | `EVENTKIT_QUEUE_MODE` | `"async"` | Queue mode (`async`, `pubsub`) |
+| | `EVENTKIT_ASYNC_WORKERS` | `4` | Number of async workers |
+| **Ring Buffer** | `EVENTKIT_RING_BUFFER_DB_PATH` | `"eventkit_ring_buffer.db"` | SQLite database path |
+| | `EVENTKIT_RING_BUFFER_MAX_SIZE` | `100000` | Max events to keep |
+
+## Protocols
+
+eventkit defines several protocols (interfaces) for extensibility.
+
+### EventStore Protocol
+
+```python
+class EventStore(Protocol):
+ async def store(self, event: TypedEvent) -> None:
+ """Store a single event."""
+
+ async def store_batch(self, events: list[TypedEvent]) -> None:
+ """Store a batch of events (more efficient)."""
+
+ def health_check(self) -> bool:
+ """Check if storage backend is healthy."""
+```
+
+**Implementations:**
+- `GCSEventStore` - Google Cloud Storage (default)
+- `FirestoreEventStore` - Firestore (development/testing)
+
+**Custom Implementation:**
+```python
+class MyCustomStore(EventStore):
+ async def store_batch(self, events: list[TypedEvent]) -> None:
+ # Your implementation
+ pass
+
+ def health_check(self) -> bool:
+ # Your implementation
+ return True
+```
+
+### EventQueue Protocol
+
+```python
+class EventQueue(Protocol):
+ async def enqueue(self, event: RawEvent) -> None:
+ """Add event to queue."""
+
+ async def start(self) -> None:
+ """Start queue processing."""
+
+ async def stop(self) -> None:
+ """Stop queue processing gracefully."""
+```
+
+**Implementations:**
+- `AsyncQueue` - In-process async workers (default)
+- `PubSubQueue` - Google Pub/Sub distributed workers
+
+### WarehouseLoader Protocol
+
+```python
+class WarehouseLoader(Protocol):
+ async def start(self) -> None:
+ """Start background loading process."""
+
+ async def stop(self) -> None:
+ """Stop background loading process."""
+
+ async def load_files(self, file_paths: list[str]) -> None:
+ """Load specific files (for manual triggering)."""
+```
+
+**Implementations:**
+- `BigQueryLoader` - GCS → BigQuery (default)
+- **Future:** `SnowflakeLoader`, `RedshiftLoader`, `ClickHouseLoader`
+
+## Event Schema
+
+### RawEvent
+
+Flexible container for any JSON payload.
+
+```python
+class RawEvent(BaseModel):
+ payload: dict[str, Any] # Original JSON
+ received_at: datetime # Ingestion timestamp
+ stream: str | None = None # Routing/isolation
+```
+
+### TypedEvent
+
+Base class for validated events.
+
+```python
+class TypedEvent(BaseModel):
+ event_id: str
+ timestamp: datetime
+ user_id: str | None
+ anonymous_id: str | None
+ event_type: str
+ properties: dict[str, Any]
+ stream: str | None
+```
+
+**Subclasses:**
+- `IdentifyEvent` - User identification
+- `TrackEvent` - User actions
+- `PageEvent` - Page views
+
+## Metrics
+
+Prometheus metrics exposed on port 9090 (configurable).
+
+```bash
+curl http://localhost:9090/metrics
+```
+
+**Key Metrics:**
+- `eventkit_events_received_total` - Events received
+- `eventkit_events_processed_total` - Events processed
+- `eventkit_events_failed_total` - Events failed validation
+- `eventkit_queue_depth` - Current queue depth
+- `eventkit_storage_bytes_written_total` - Bytes written
+
+See **[Monitoring](/advanced/monitoring)** for complete metrics reference.
+
+## Next Steps
+
+- **[Configuration Reference](/api/configuration)** - Complete environment variable reference
+- **[Protocols](/api/protocols)** - Implement custom backends
+- **[Local Development](/guides/local-development)** - Try the API locally
diff --git a/docs/content/contributing/index.mdx b/docs/content/contributing/index.mdx
new file mode 100644
index 0000000..a8022f6
--- /dev/null
+++ b/docs/content/contributing/index.mdx
@@ -0,0 +1,537 @@
+---
+title: Contributing
+description: Guide to contributing to eventkit
+---
+
+# Contributing to eventkit
+
+Thank you for your interest in contributing to **eventkit**! This guide will help you get started.
+
+## Code of Conduct
+
+This project follows the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). By participating, you are expected to uphold this code.
+
+## Getting Started
+
+### Prerequisites
+
+- Python 3.11 or 3.12
+- Git
+- Google Cloud SDK (for Firestore emulator)
+
+### Fork and Clone
+
+1. Fork the repository on GitHub
+2. Clone your fork:
+
+```bash
+git clone https://github.com/YOUR_USERNAME/eventkit.git
+cd eventkit
+```
+
+3. Add upstream remote:
+
+```bash
+git remote add upstream https://github.com/prosdev/eventkit.git
+```
+
+### Install Dependencies
+
+```bash
+# Install in editable mode with dev dependencies
+pip install -e ".[dev]"
+
+# Set up pre-commit hooks (optional but recommended)
+pre-commit install
+```
+
+### Start Firestore Emulator (for integration tests)
+
+```bash
+gcloud emulators firestore start --host-port=localhost:8080
+```
+
+In another terminal:
+```bash
+export FIRESTORE_EMULATOR_HOST=localhost:8080
+```
+
+### Verify Setup
+
+```bash
+# Run tests
+pytest
+
+# Type check
+mypy src/eventkit
+
+# Lint
+ruff check src/
+```
+
+## Development Workflow
+
+We follow **Spec-Driven Development** (inspired by [GitHub's spec-kit](https://github.com/github/spec-kit)):
+
+1. **Spec** → Define what to build (`specs/*/spec.md`)
+2. **Plan** → Define how to build it (`specs/*/plan.md`)
+3. **Tasks** → Break into atomic tasks (`specs/*/tasks.md`)
+4. **Implement** → Build it, following TDD
+
+See [Spec-Driven Development](/contributing/spec-process) for detailed process.
+
+### Working on a Feature
+
+1. **Check existing specs** in `specs/` directory
+2. **Pick a task** from `specs/*/tasks.md`
+3. **Create a branch**:
+
+```bash
+git checkout -b feature/P1-T001-raw-event-model
+```
+
+4. **Write tests first** (TDD):
+
+```bash
+# Create test file
+touch tests/unit/schema/test_raw.py
+
+# Write failing tests
+# Then implement to make them pass
+```
+
+5. **Implement the feature**
+6. **Run tests**:
+
+```bash
+pytest tests/unit/schema/test_raw.py -v
+```
+
+7. **Check quality**:
+
+```bash
+# Type check
+mypy src/eventkit
+
+# Lint
+ruff check src/
+
+# Format
+ruff format src/
+```
+
+8. **Commit with conventional commit message** (see below)
+9. **Push and create PR**
+
+## Project Structure
+
+```
+eventkit/
+├── src/
+│ └── eventkit/ # Source code
+│ ├── schema/ # Data models (RawEvent, TypedEvent)
+│ ├── adapters/ # Event adapters and validators
+│ ├── processing/ # Sequencer, buffer, processor
+│ ├── stores/ # Storage interfaces and implementations
+│ ├── api/ # FastAPI routes
+│ ├── logging/ # Structured logging
+│ └── errors/ # Custom exceptions
+├── tests/
+│ ├── unit/ # Fast, isolated tests
+│ ├── integration/ # Multi-component tests
+│ └── performance/ # Throughput and latency tests
+├── specs/ # Specifications and plans
+│ └── core-pipeline/
+│ ├── spec.md # User stories
+│ ├── plan.md # Implementation plan
+│ └── tasks.md # Task breakdown
+├── examples/ # Usage examples
+├── CLAUDE.md # AI agent context
+├── WORKFLOW.md # Spec-driven workflow
+├── TESTING.md # Testing guide
+└── CONTRIBUTING.md # This file
+```
+
+## Coding Standards
+
+### Python Style
+
+- **PEP 8** compliant (enforced by `ruff`)
+- **Type hints required** (enforced by `mypy` in strict mode)
+- **Docstrings** for public APIs (Google style)
+- **Maximum line length**: 100 characters
+
+### Code Patterns
+
+#### 1. Use Protocols over Abstract Base Classes
+
+✅ **Good**:
+```python
+from typing import Protocol
+
+class EventStore(Protocol):
+ async def write(self, events: list[TypedEvent]) -> None: ...
+```
+
+❌ **Avoid**:
+```python
+from abc import ABC, abstractmethod
+
+class EventStore(ABC):
+ @abstractmethod
+ async def write(self, events: list[TypedEvent]) -> None: ...
+```
+
+#### 2. Use Pydantic v2 Patterns
+
+✅ **Good**:
+```python
+from pydantic import BaseModel, ConfigDict, Field
+
+class RawEvent(BaseModel):
+ model_config = ConfigDict(extra="allow")
+ payload: dict[str, Any]
+```
+
+❌ **Avoid**:
+```python
+class RawEvent(BaseModel):
+ class Config:
+ extra = "allow" # Pydantic v1 syntax
+```
+
+#### 3. Never Reject at Edge
+
+✅ **Good**:
+```python
+def adapt(self, raw: RawEvent) -> AdapterResult:
+ if not self._is_valid(raw):
+ return AdapterResult.err("Invalid event") # Return error
+ return AdapterResult.ok(event)
+```
+
+❌ **Avoid**:
+```python
+def adapt(self, raw: RawEvent) -> TypedEvent:
+ if not self._is_valid(raw):
+ raise ValidationError("Invalid event") # Never raise in hot path
+```
+
+#### 4. Async Throughout
+
+✅ **Good**:
+```python
+async def enqueue(self, event: RawEvent) -> None:
+ await self.processor.enqueue(event)
+```
+
+❌ **Avoid**:
+```python
+def enqueue(self, event: RawEvent) -> None:
+ # Blocking I/O
+ self.store.write(event)
+```
+
+#### 5. Explicit is Better than Implicit
+
+✅ **Good**:
+```python
+def get_partition_id(self, event: TypedEvent) -> int:
+ """Route event to partition by identity hash.
+
+ Uses FNV-1a hash for good distribution. Events with same userId
+ always route to same partition for ordered processing.
+ """
+ routing_key = self._get_routing_key(event)
+ hash_value = self._fnv1a_hash(routing_key)
+ return hash_value % self.num_partitions
+```
+
+❌ **Avoid**:
+```python
+def get_partition_id(self, event: TypedEvent) -> int:
+ # Hash and modulo
+ return hash(event.userId or event.anonymousId) % self.num_partitions
+```
+
+## Testing Guidelines
+
+See [Testing Guidelines](/contributing/testing) for comprehensive testing guide.
+
+### Quick Reference
+
+- **Unit tests**: Fast, isolated, mock dependencies
+- **Integration tests**: Multi-component, use Firestore emulator
+- **Performance tests**: Validate throughput/latency targets
+- **Coverage target**: >80%
+
+### Test-Driven Development (TDD)
+
+**Always write tests before implementation**:
+
+```bash
+# 1. Write failing test
+# tests/unit/schema/test_raw.py
+def test_raw_event_accepts_arbitrary_fields():
+ event = RawEvent(payload={"custom": "field"}, stream="test")
+ assert event.get("custom") == "field"
+
+# 2. Run test (should fail)
+pytest tests/unit/schema/test_raw.py::test_raw_event_accepts_arbitrary_fields
+
+# 3. Implement feature
+# src/eventkit/schema/raw.py
+class RawEvent(BaseModel):
+ # ... implementation
+
+# 4. Run test (should pass)
+pytest tests/unit/schema/test_raw.py::test_raw_event_accepts_arbitrary_fields
+```
+
+## Commit Message Guidelines
+
+We use [Conventional Commits](https://www.conventionalcommits.org/) enforced by `gitlint`.
+
+### Format
+
+```
+():
+
+
+
+