diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..b8f6019
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,44 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ unit-tests:
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.12
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v4
+
+ - name: Create venv and install dependencies
+ run: uv venv && uv pip install -e '.[test]'
+
+ - name: Lint with ruff
+ run: uv run ruff check src/ tests/
+
+ - name: Run unit tests
+ run: |
+ uv run python -m pytest tests/ -x -q \
+ --ignore=tests/test_smoke_pipelines.py \
+ --ignore=tests/test_smoke_agent.py \
+ --ignore=tests/test_integration_extract.py \
+ --ignore=tests/test_integration_summarize.py \
+ --ignore=tests/test_integration_agent.py \
+ --ignore=tests/test_integration_providers.py \
+ --ignore=tests/test_e2e_sync.py \
+ --ignore=tests/test_e2e_maintain.py \
+ --ignore=tests/test_e2e_full_cycle.py \
+ -m "not smoke and not integration and not e2e"
diff --git a/.gitignore b/.gitignore
index 3b4ac5b..6d8e6e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,15 +32,23 @@ site/
.tmp/
specs/
.lerim/
+.playwright-cli/
+.logfire/
+.pytest_cache/
+.ruff_cache/
+.claude/
+
dev-docs/
# Test artifacts
snapshot_report.html
qa_artifacts/*
+.pytest_tmp/
# Local browser/db state
orb.db/
AGENTS.md
CLAUDE.md
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..6c69c5e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,29 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.1.0] - 2026-02-25
+
+### Added
+
+- Continual learning layer for coding agents and projects.
+- Platform adapters for Claude Code, Codex CLI, Cursor, and OpenCode.
+- Memory extraction pipeline using DSPy RLM to extract decisions and learnings from coding session traces.
+- Trace summarization pipeline using DSPy RLM to produce structured summaries with YAML frontmatter.
+- PydanticAI lead agent with a read-only explorer subagent for memory operations.
+- Three CLI flows: `sync` (extract, summarize, write memories), `maintain` (merge, archive, decay), and `chat` (query memories).
+- Daemon mode for continuous sync and maintain loop.
+- Local read-only web dashboard.
+- Session catalog with SQLite FTS5 for session search.
+- Job queue with stale job reclamation.
+- TOML-layered configuration: shipped defaults, global, project, and env var override.
+- OpenTelemetry tracing via Logfire with PydanticAI and DSPy instrumentation.
+- Multi-provider LLM support: OpenRouter (with Nebius routing), Ollama, ZAI, OpenAI, Anthropic.
+- File-first memory model using markdown files with YAML frontmatter.
+- Project-first memory scope with global fallback.
+- Memory primitives: decisions, learnings, and summaries.
+- Comprehensive test suite with 290 tests across unit, smoke, integration, and e2e layers.
+- Skills distribution via `npx skills add lerim-dev/lerim-cli`.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..a466316
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,95 @@
+# Contributing to Lerim
+
+Lerim is licensed under BSL 1.1. By contributing you agree your changes
+fall under the same license (1 user free, 2+ users need a commercial license).
+
+## Dev environment setup
+
+Requires Python 3.12+.
+
+```bash
+uv venv && source .venv/bin/activate
+uv pip install -e '.[test]'
+```
+
+## Running tests
+
+```bash
+# Unit tests (no LLM keys needed)
+tests/run_tests.sh unit
+
+# Smoke tests (needs LLM API key in env)
+tests/run_tests.sh smoke
+
+# Everything
+tests/run_tests.sh all
+```
+
+Lint before submitting:
+
+```bash
+ruff check src/ tests/
+```
+
+## Coding style
+
+Full rules live in `docs/simple-coding-rules.md`. The short version:
+
+- **Minimal code.** Prefer fewer functions, fewer layers, fewer lines.
+- **Strict schemas.** Use Pydantic models / enums for inputs and outputs.
+- **Fail fast.** No `try/except` fallbacks for missing packages. If something
+ is required, let it raise.
+- **Docstrings everywhere.** Every file gets a top-level docstring explaining
+ what it contains. Every function gets a docstring.
+- **Self-tests.** Add an `if __name__ == "__main__":` block that exercises the
+ real code path (no mocking or stubbing). Keep it inline, not in a separate
+ function.
+- **No dead code.** When you replace logic, remove the old path.
+- **Config from TOML, keys from env.** Runtime config comes from the TOML
+ layer stack (`default.toml -> global -> project -> env var`). Only API keys
+ use environment variables.
+
+## Adding a new platform adapter
+
+This is the most common contribution. Follow these steps:
+
+1. **Create `src/lerim/adapters/.py`.**
+ Start with a top-level docstring. Implement the four functions required by
+ the `Adapter` protocol in `src/lerim/adapters/base.py`:
+
+ - `default_path() -> Path | None` -- where traces live on disk.
+ - `count_sessions(path) -> int`
+ - `iter_sessions(traces_dir, start, end, known_run_ids) -> list[SessionRecord]`
+ - `find_session_path(session_id, traces_dir) -> Path | None`
+ - `read_session(session_path, session_id) -> ViewerSession | None`
+
+ See an existing adapter (e.g. `codex.py` or `claude.py`) as a reference.
+
+2. **Register the adapter** in `src/lerim/adapters/registry.py`:
+ add an entry to `_ADAPTER_MODULES` and optionally to `_AUTO_SEED_PLATFORMS`.
+
+3. **Add a self-test** (`if __name__ == "__main__":` block) at the bottom of
+ the new adapter file that exercises the real code path.
+
+4. **Add unit tests** in `tests/test__adapter.py`. Look at the
+ existing adapter test files for the expected patterns.
+
+5. **Update `tests/README.md`** if you added new fixtures or test infrastructure.
+
+## Reporting bugs
+
+Open a GitHub issue with:
+
+- Steps to reproduce.
+- Expected vs actual behavior.
+- Lerim version (`python -m lerim --version`), Python version, and OS.
+- Relevant config (redact API keys).
+
+## Pull request checklist
+
+- [ ] `ruff check src/ tests/` passes with no errors.
+- [ ] `tests/run_tests.sh unit` passes.
+- [ ] New/changed files have top-level docstrings and function docstrings.
+- [ ] New source files include an `if __name__ == "__main__":` self-test when practical.
+- [ ] No mocking or stubbing in self-test flows.
+- [ ] Related docs updated if behavior changed.
diff --git a/README.md b/README.md
index f123d0b..a9401a4 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Lerim is a continual learning layer that gives coding agents persistent memory across sessions. It watches your agent conversations (Claude Code, Codex, Cursor, OpenCode, ...), extracts decisions and learnings, and stores them as plain markdown files that both humans and agents can read. Memories are refined offline over time through merging, deduplication, archiving, and decay-based forgetting. You can query stored memories anytime to bring relevant past context into your current session.
@@ -16,7 +16,7 @@ Lerim is file-first and primitive-first.
- Global fallback memory: `~/.lerim/`
- Search default: `files` (no index required)
- Orchestration runtime: `pydantic-ai` lead agent + read-only explorer subagent
-- Extraction/summarization: `dspy.RLM` role-configured models (default Ollama `qwen3:8b`)
+- Extraction/summarization: `dspy.RLM` role-configured models (default OpenRouter `x-ai/grok-4.1-fast`)
- Graph source of truth: explicit id/slug references (and `related` when present)
This keeps memory readable by humans and easy for agents to traverse.
@@ -29,6 +29,22 @@ Lead flow:
4. Lead writes memory only through boundary-enforced runtime write/edit tools.
5. `sync` stays lightweight; `maintain` runs offline memory refinement (merge duplicates, archive low-value entries, consolidate related memories, apply time-based decay).
+### Sync path
+
+
+
+
+
+The sync path processes new agent sessions: reads transcript archives, extracts decision and learning candidates via DSPy, deduplicates against existing memories, and writes new primitives to the memory folder.
+
+### Maintain path
+
+
+
+
+
+The maintain path runs offline refinement over stored memories: merges duplicates, archives low-value entries, consolidates related memories, and applies time-based decay to keep the memory store clean and relevant.
+
## Quick start
### 1. Install
@@ -70,6 +86,39 @@ At the start of a session, tell your agent:
Your agent will run `lerim chat` or `lerim memory search` to pull in past decisions and learnings before it starts working.
+## Dashboard
+
+The dashboard gives you a local UI for session analytics, memory browsing, and runtime status.
+
+