Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7c5b128
feat(impact): add Impact Evaluation domain foundation (Phase 13A)
Sequela02 Feb 14, 2026
34e13ee
fix(docs): sync landing page to 78 tools, 19 sources, 4 domains
Sequela02 Feb 14, 2026
576a87f
feat(impact): add DiD estimator, threat assessment, and console templ…
Sequela02 Feb 14, 2026
aaa5ac5
feat(impact): add 3 viz tools and sync all docs for Phase 13
Sequela02 Feb 14, 2026
46ba33b
feat(analysis): add domain-agnostic causal inference (DiD, PSM, RDD, SC)
Sequela02 Feb 14, 2026
23aeebd
refactor(investigation): decompose multi_orchestrator.py into focused…
Sequela02 Feb 14, 2026
e0d78cb
refactor(investigation): fix Phase 14A review issues
Sequela02 Feb 14, 2026
5018315
feat(investigation): add document upload for CSV/XLSX/PDF (Phase 13A-2)
Sequela02 Feb 14, 2026
e273231
refactor(docs): reorganize docs into roadmap/, research/, and adr/ fo…
Sequela02 Feb 14, 2026
f483de2
refactor(docs): deduplicate CLAUDE.md, trim CONTRIBUTING.md, replace …
Sequela02 Feb 14, 2026
e013d78
docs: add index READMEs for docs/ and docs/adr/
Sequela02 Feb 14, 2026
3163d5d
docs: align identity as COSS (Commercial Open-Source Software) - READ…
Sequela02 Feb 14, 2026
520bc75
refactor(14B): extract registry factory, orchestrator factory, and sc…
Sequela02 Feb 14, 2026
b52ad69
refactor(prompts): split prompts.py into prompts/ package (14C)
Sequela02 Feb 14, 2026
a08ed28
refactor(14A): decompose orchestrator run() into phase_runner + liter…
Sequela02 Feb 14, 2026
e98e4ce
feat(14H): harden upload validation with domain constants and client-…
Sequela02 Feb 14, 2026
015f176
feat(14D): enrich domain tools with filtering, ranking, and compariso…
Sequela02 Feb 14, 2026
7210242
feat(api): enforce investigation ownership on all per-resource endpoints
Sequela02 Feb 14, 2026
eaa8d58
fix(investigation): resolve mypy errors in orchestrator and registry …
Sequela02 Feb 14, 2026
aae5f3b
docs(web): align landing page with COSS identity and fix stale counts
Sequela02 Feb 14, 2026
72a38e7
feat(14F-2): add paper view route with print-optimized PDF export
Sequela02 Feb 14, 2026
5430866
feat(console): add enhanced investigation comparison with findings di…
Sequela02 Feb 14, 2026
3cbc196
chore: fix pyproject.toml metadata and remove unused deps
Sequela02 Feb 14, 2026
1979c76
feat(14E): add agentic hypothesis tree search with TreeManager
Sequela02 Feb 14, 2026
5301c60
refactor(console): flatten molecule feature folder, remove components…
Sequela02 Feb 14, 2026
d2cd5b1
feat(13D): add 7 US federal data API clients, 5 new tools, extend eco…
Sequela02 Feb 15, 2026
0368e10
feat(api): add security hardening across server and console
Sequela02 Feb 15, 2026
c321669
feat(ui): Console Hardening, Methodology Showcase 2.0, Home Redesign
Sequela02 Feb 15, 2026
e9ff466
fix(api): add 60s leeway to JWT validation for clock skew tolerance
Sequela02 Feb 15, 2026
3a4f249
feat(13C): add Mexico Integration (INEGI, Banxico, datos.gob.mx) + co…
Sequela02 Feb 15, 2026
c0d3df3
chore: update .gitignore add .gemini/, *.log, untrack routeTree.gen.ts
Sequela02 Feb 16, 2026
2295a8e
feat(investigation): implement state machine with pause/cancel/approval
Sequela02 Feb 16, 2026
709914f
feat(investigation): migrate diagram visualization from debug to prod…
Sequela02 Feb 16, 2026
a874c17
feat(investigation): add SSE Last-Event-ID dedup + engine hardening
Sequela02 Feb 16, 2026
53bb602
feat(literature): optimize token usage with dedup, field trimming, an…
Sequela02 Feb 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ data/datasets/
data/models/
server/data/

# Claude Code
# Agent tooling
.claude/
.gemini/

# Logs
*.log

# Environment
.env
Expand Down
283 changes: 2 additions & 281 deletions CHANGELOG.md

Large diffs are not rendered by default.

507 changes: 115 additions & 392 deletions CLAUDE.md

Large diffs are not rendered by default.

144 changes: 31 additions & 113 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,117 +2,39 @@

Ehrlich is an AI-powered scientific discovery engine that uses Claude as a hypothesis-driven reasoning engine. The core engine is domain-agnostic -- any scientific domain can plug in with its own tools, scoring, and visualization.

See [docs/domain-agnostic.md](docs/domain-agnostic.md) for the full story of how and why the engine was generalized.
See [README.md](README.md) for setup, architecture overview, and development commands.
See [CLAUDE.md](CLAUDE.md) for dependency rules, key patterns, and key files.

## Prerequisites
## Contributor License Agreement

- **Python 3.12+** with [uv](https://docs.astral.sh/uv/) package manager
- **Bun** (or Node 20+) for the console
- **ANTHROPIC_API_KEY** environment variable set
By submitting a pull request to this repository, you agree to the following terms:

## Setup
1. **You grant Ehrlich's maintainers a perpetual, worldwide, non-exclusive, royalty-free license** to use, reproduce, modify, distribute, sublicense, and relicense your contributions as part of this project.

### Server
2. **You retain copyright** to your contributions. This agreement does not transfer ownership.

```bash
cd server
uv sync --extra dev
uv run uvicorn ehrlich.api.app:create_app --factory --reload --port 8000
```

### Console

```bash
cd console
bun install
bun dev # Dev server at :5173
```
3. **You have the right to submit** the contribution. If your contribution includes code from your employer or third parties, you have obtained the necessary permissions.

## Development Workflow
4. **Your contribution is provided "as is"** without warranty of any kind.

### Quality Gates (all must pass before commit)
### Why This Matters

**Server:**
```bash
uv run ruff check src/ tests/ # Lint (zero violations)
uv run ruff format src/ tests/ --check # Format check
uv run mypy src/ehrlich/ # Type check (strict)
uv run pytest --cov=ehrlich # Tests (80%+ coverage)
```

**Console:**
```bash
bun run build # Vite build (generates route tree)
bun run typecheck # tsc --noEmit (run after build)
bunx vitest run # Tests
```

### Commit Format

```
type(scope): description
```
Ehrlich is currently licensed under AGPL-3.0. This CLA preserves the option to change the license in the future (e.g., to MIT) without requiring permission from every past contributor. This is standard practice for open source projects with dual-licensing strategies.

**Types:** `feat`, `fix`, `refactor`, `chore`, `docs`, `test`
If you have questions about this CLA, open an issue before contributing.

**Scopes:** `kernel`, `shared`, `literature`, `chemistry`, `analysis`, `prediction`, `simulation`, `training`, `nutrition`, `investigation`, `api`, `console`, `mol`, `data`

### Code Standards (Non-Negotiable)
---

Every contribution must respect:
## Code Standards (Non-Negotiable)

- **DDD + Clean Architecture** -- Respect bounded contexts. `domain/` is pure Python with zero external deps. `application/` depends on domain interfaces, never infrastructure. `infrastructure/` implements domain ABCs. No cross-context domain imports.
- **No redundancy** -- DRY across layers. If a pattern exists, reuse it. If a utility exists, find it before writing a new one. Three similar lines of code is better than a premature abstraction, but true duplication is never acceptable.
- **No dead code** -- No commented-out blocks, no unused imports, no orphaned functions. Delete what you don't need.
- **No boilerplate** -- If it can be derived (schemas from type hints, routes from file conventions), derive it. Don't write what the framework gives you.
- **No backward-compatibility hacks** -- No renamed `_unused` vars, no re-exports for removed code, no `# removed` comments. If something is unused, delete it. Exception: production hotfixes with a follow-up cleanup ticket.
- **Clean Code** -- Readability over cleverness. Explicit imports only (no wildcards). Fail fast at boundaries. Meaningful names. Small focused functions.
- **Documentation as code** -- Every code change updates relevant docs in the same commit. Tool counts, event counts, file tables, component lists. This is the #1 recurring failure -- never skip it.
- **DDD + Clean Architecture** -- Respect bounded contexts. See CLAUDE.md for the 8 strict dependency rules.
- **No redundancy** -- DRY across layers. Three similar lines is better than a premature abstraction, but true duplication is never acceptable.
- **No dead code** -- No commented-out blocks, no unused imports, no orphaned functions.
- **No backward-compatibility hacks** -- If something is unused, delete it.
- **Clean Code** -- Readability over cleverness. Explicit imports only (no wildcards). Fail fast at boundaries.
- **Documentation as code** -- Every code change updates relevant docs in the same commit. Tool counts, event counts, file tables, component lists.
- **Test before claiming complete** -- All quality gates must pass. Never say "done" without running the full suite.

## Architecture

DDD monorepo with 10 bounded contexts. Each context follows `domain/` -> `application/` -> `infrastructure/` layering.

### Layer Rules (Strict)

1. `domain/` has ZERO external dependencies -- pure Python only
2. `application/` depends on `domain/` interfaces, never on `infrastructure/`
3. `infrastructure/` implements `domain/` repository interfaces
4. `tools.py` calls `application/` services, returns JSON for Claude
5. No cross-context domain imports -- communicate via `kernel/` primitives or `shared/` ports
6. `shared/` contains cross-cutting ports (ABCs) and value objects -- no infrastructure deps
7. RDKit imports ONLY in `chemistry/infrastructure/rdkit_adapter.py`

### Multi-Model Orchestrator

```
Opus 4.6 (Director) -- Formulates hypotheses, evaluates evidence (NO tools)
Sonnet 4.5 (Researcher) -- Executes experiments with 70 tools (parallel: 2 per batch)
Haiku 4.5 (Summarizer) -- Compresses large outputs, classifies domains
```

### Bounded Contexts

| Context | Domain | Purpose |
|---------|--------|---------|
| kernel | All | Shared primitives (SMILES, Molecule, exceptions) |
| shared | All | Cross-cutting ports and value objects (ChemistryPort, Fingerprint, Conformer3D) |
| literature | All | Paper search (Semantic Scholar) |
| chemistry | Molecular | RDKit cheminformatics |
| analysis | Molecular | Dataset exploration (ChEMBL, PubChem, GtoPdb) |
| prediction | Molecular | ML models (XGBoost, Chemprop) |
| simulation | Molecular | Docking, ADMET, targets (RCSB PDB, UniProt, Open Targets, EPA CompTox) |
| training | Training | Exercise physiology (evidence analysis, protocol comparison, injury risk, training metrics, clinical trials) |
| nutrition | Nutrition | Nutrition science (supplement evidence, labels, nutrients, safety, interactions, adequacy, inflammatory index) |
| investigation | All | Multi-model orchestration + domain registry + SQLite persistence |

## Testing

- Server: `uv run pytest` -- unit + integration tests
- Console: `bunx vitest run` -- component + utility tests
- Coverage minimum: 80% (server), enforced by pytest-cov

---

## Adding a New Scientific Domain
Expand All @@ -125,23 +47,23 @@ Before writing any code, research the domain deeply. This upfront work determine

**What to research:**

1. **Problem definition** -- What scientific questions should this domain answer? What does "discovery" mean here? (e.g., molecular: find candidate drugs; training: find optimal training protocols)
1. **Problem definition** -- What scientific questions should this domain answer? What does "discovery" mean here?
2. **Data sources** -- Where does real experimental data come from? Identify free APIs, databases, or datasets. For each source: base URL, auth requirements, rate limits, response format, coverage
3. **Candidate criteria** -- What makes a good candidate in this domain? What scores matter? What thresholds separate good from bad? (e.g., molecular: prediction_score > 0.7, docking < -7.0 kcal/mol; training: effect_size > 0.5, evidence_grade A-B)
3. **Candidate criteria** -- What makes a good candidate in this domain? What scores matter? What thresholds separate good from bad?
4. **Hypothesis types** -- What kinds of hypotheses does this domain test? (mechanistic, structural, correlational, physiological, etc.)
5. **Visualization needs** -- How should results be displayed? Charts, 3D models, diagrams, maps, tables?
6. **Domain vocabulary** -- Key terms, units, standard notations that Claude needs in its prompts

**Document your research** in `research/{yourdomain}/`:
**Document your research** in `docs/research/{yourdomain}/`:

```
research/yourdomain/
docs/research/yourdomain/
data-sources.md # APIs, databases, auth, rate limits
domain-model.md # Entities, scoring criteria, thresholds
prior-work.md # Existing tools and approaches in this field
```

See `research/molecular/`, `research/methodology/`, and the `training/` and `nutrition/` bounded contexts for examples of thorough domain research and implementation.
See `docs/research/molecular/`, `docs/research/methodology/`, and the `training/` and `nutrition/` bounded contexts for examples.

### Step 1: Create the Bounded Context

Expand Down Expand Up @@ -221,15 +143,14 @@ YOUR_DOMAIN = DomainConfig(
),
attribute_keys=("category", "source"), # Non-numeric candidate attributes
negative_control_threshold=0.5, # Score below = correctly classified
# visualization is reactive: LiveLabViewer auto-appears for molecular tools,
hypothesis_types=(
"mechanistic", # Valid hypothesis_type values for this domain
"correlational",
"predictive",
),
valid_domain_categories=(
"your_domain", # Categories the classifier might output
"related_field", # Map these to your domain
"related_field",
),
template_prompts=(
{
Expand All @@ -245,9 +166,9 @@ YOUR_DOMAIN = DomainConfig(
```

Key decisions:
- **Visualization**: Visualization is reactive -- LiveLabViewer auto-appears when molecular tool events (docking, descriptors, etc.) are detected in the SSE stream. Chart visualizations render inline from `VisualizationRendered` events. No configuration needed.
- **Visualization**: Visualization is reactive -- LiveLabViewer auto-appears when molecular tool events are detected in the SSE stream. Chart visualizations render inline from `VisualizationRendered` events. No configuration needed.
- **`tool_tags`**: This frozenset determines which tools the Researcher can see. Include `"literature"` if you want paper search.
- **`director_examples`**: These are the most impactful part. Good multishot examples make the Director formulate domain-appropriate hypotheses. Use `{{` and `}}` for literal braces in f-string-like contexts.
- **`director_examples`**: These are the most impactful part. Good multishot examples make the Director formulate domain-appropriate hypotheses.
- **`score_definitions`**: These become the columns in the CandidateTable. The frontend auto-generates color-coded cells based on thresholds.

### Step 4: Register Tools and Domain
Expand Down Expand Up @@ -383,12 +304,9 @@ Update these files (in the same commit as the code):

| File | What to update |
|------|---------------|
| `CLAUDE.md` | Tool count, bounded contexts table, tools section, scopes list |
| `CLAUDE.md` | Bounded contexts table, key files |
| `README.md` | Tool count, bounded contexts table, tools table, "What Can Ehrlich Investigate" |
| `docs/architecture.md` | Bounded contexts, tool count, data flow |
| `docs/roadmap.md` | Add completion entry |
| `web/src/lib/constants.ts` | STATS counts, DOMAINS array (toolCount, capabilities, sources, vizTools), DATA_SOURCES array |
| `console/` | Template prompts, domain badges, tool references where applicable |
| `web/src/lib/constants.ts` | STATS counts, DOMAINS array, DATA_SOURCES array |

### Step 9: Verify

Expand Down Expand Up @@ -416,8 +334,8 @@ That's it. Your domain is live. The orchestrator will auto-detect it from the us
2. Register it in `_build_registry()` with the domain's tag
3. Write tests
4. Update the integration test tool count in `tests/integration/test_e2e.py`
5. Update docs (tool counts in CLAUDE.md, README.md, docs/architecture.md)
6. Update tool counts and references in `web/src/lib/constants.ts` (STATS, DOMAINS toolCount) and `console/` where applicable
5. Update docs (tool counts in CLAUDE.md, README.md)
6. Update tool counts and references in `web/src/lib/constants.ts` (STATS, DOMAINS toolCount)

### Improving Score Definitions

Expand Down
Loading
Loading