# Customization guide

Research Hub ships with a default category system tuned for flood risk + agent-based modeling research, because that is where it was first used. The pipeline itself is domain-agnostic. This guide shows how to adapt it to other research areas.

Three things typically need to change:

1. **Zotero collection map** — the hierarchy of collections you want papers organized into
2. **Obsidian sub-category folders** — the `raw/<sub-category>/` layout the pipeline writes into
3. **NotebookLM notebook names** — which notebooks papers get uploaded to

All three live in `config.json`. Source code changes are only needed if you are adding a new step or a new external service.

---

## The `config.json` schema

```json
{
  "knowledge_base": {
    "root": "~/vault/research-hub",
    "raw": "~/vault/research-hub/raw",
    "hub": "~/vault/research-hub/hub",
    "projects": "~/vault/research-hub/projects",
    "logs": "~/vault/research-hub/logs",
    "obsidian_graph": "~/vault/research-hub/.obsidian/graph.json"
  },
  "zotero": {
    "library_id": "YOUR_ZOTERO_USER_ID",
    "library_type": "user",
    "default_collection": "ABCD1234",
    "collections": {
      "ABCD1234": {
        "name": "Survey Papers",
        "parent": null,
        "section": "survey"
      }
    }
  }
}
```

| Key | Meaning |
|---|---|
| `knowledge_base.root` | Obsidian vault root. Everything else defaults under this if not explicitly set. |
| `knowledge_base.raw` | Per-paper markdown notes. Sub-categories live one level deeper. |
| `knowledge_base.hub` | Generated index pages. Regenerated by `vault/builder.py`. |
| `knowledge_base.projects` | Project-specific folders (optional). |
| `knowledge_base.logs` | Where the pipeline writes run logs and error JSONL files. |
| `knowledge_base.obsidian_graph` | Path to `graph.json` so `categorize.py` can recolor nodes. |
| `zotero.library_id` | Your numeric user ID from <https://www.zotero.org/settings/keys>. |
| `zotero.library_type` | `user` or `group`. Defaults to `user`. |
| `zotero.default_collection` | The collection key that new papers get saved to unless the LLM classifier picks another one. |
| `zotero.collections` | A map from collection key to `{name, parent, section}`. The pipeline reads this to avoid hardcoding keys in source. |

Secrets (API key) are **not** in `config.json`. They live in `ZOTERO_API_KEY` env var or `~/.claude/.env`.

---

## Worked example 1 — Chemistry lab

You run a synthetic chemistry lab focused on catalyst design and reaction mechanisms. You want Research Hub to organize papers into:

- `catalysts` — heterogeneous and homogeneous catalyst papers
- `mechanisms` — mechanistic studies and kinetic analyses
- `synthesis-routes` — total synthesis and retrosynthesis papers
- `characterization` — spectroscopy, crystallography methods

Your `config.json` zotero section:

```json
"zotero": {
  "library_id": "1234567",
  "library_type": "user",
  "default_collection": "CAT00001",
  "collections": {
    "CAT00001": {
      "name": "Catalysts",
      "parent": null,
      "section": "catalysts"
    },
    "CAT00002": {
      "name": "Heterogeneous",
      "parent": "CAT00001",
      "section": "catalysts"
    },
    "CAT00003": {
      "name": "Homogeneous",
      "parent": "CAT00001",
      "section": "catalysts"
    },
    "MEC00001": {
      "name": "Reaction Mechanisms",
      "parent": null,
      "section": "mechanisms"
    },
    "SYN00001": {
      "name": "Synthesis Routes",
      "parent": null,
      "section": "synthesis-routes"
    },
    "CHR00001": {
      "name": "Characterization",
      "parent": null,
      "section": "characterization"
    }
  }
}
```

Your Obsidian layout:

```
vault/
├── raw/
│   ├── catalysts/
│   ├── mechanisms/
│   ├── synthesis-routes/
│   └── characterization/
└── hub/
    └── (generated)
```

Update `references/categories.md` so the LLM classifier knows what each section means. Add section headers like:

```markdown
## catalysts
Papers on heterogeneous or homogeneous catalyst design, activity, selectivity, stability, and spectroscopic characterization of active sites.

## mechanisms
Mechanistic and kinetic studies — DFT, isotope labeling, intermediate trapping, rate law analysis.
```

NotebookLM: create two notebooks named `Chemistry Main` and `Chemistry Methods`, then update the skill's NotebookLM routing table in `skills/knowledge-base/SKILL.md` Step 5 so that `catalysts` / `mechanisms` / `synthesis-routes` go to `Chemistry Main` and `characterization` goes to `Chemistry Methods`.

---

## Worked example 2 — Economics survey research group

You run a lab doing survey-based behavioral economics. You want:

- `field-experiments` — RCTs, lab-in-field studies
- `survey-methods` — questionnaire design, sampling, SEM
- `behavioral-theory` — prospect theory, inequity aversion, time preferences
- `policy-evaluation` — DiD, RDD, IV studies

Your `config.json` zotero section:

```json
"zotero": {
  "library_id": "7654321",
  "library_type": "user",
  "default_collection": "EXP00001",
  "collections": {
    "EXP00001": {
      "name": "Field Experiments",
      "parent": null,
      "section": "field-experiments"
    },
    "SRV00001": {
      "name": "Survey Methods",
      "parent": null,
      "section": "survey-methods"
    },
    "THY00001": {
      "name": "Behavioral Theory",
      "parent": null,
      "section": "behavioral-theory"
    },
    "POL00001": {
      "name": "Policy Evaluation",
      "parent": null,
      "section": "policy-evaluation"
    }
  }
}
```

For this domain, Step 2 (search) benefits from turning PubMed off in favor of SSRN and NBER, which `paper-search-mcp` already supports via CrossRef. No config change needed — Step 2 in `skills/knowledge-base/SKILL.md` always runs CrossRef when the topic looks non-medical.

NotebookLM: one notebook is enough for most economics labs. Name it `Econ Literature`.

---

## Adding a new sub-category

Four concrete steps:

1. **Create the folder** — `mkdir <vault-root>/raw/<new-sub-category>`
2. **Add to the Zotero collection map** in `config.json`:
   ```json
   "XXXX0001": {
     "name": "Your New Topic",
     "parent": null,
     "section": "<new-sub-category>"
   }
   ```
3. **Update `references/categories.md`** — add a section describing what papers fit under the new sub-category. The LLM classifier reads this file during Step 1.
4. **Rebuild the hub** — run `python -m research_hub.vault.builder` (or use the `research-hub build` console script once the CLI wiring is finished).

---

## Replacing NotebookLM with local RAG

Step 5 is optional. If you don't have NotebookLM access, or you don't want to depend on Chrome automation, you can skip it entirely and run a local RAG tool over your `raw/` folder instead.

The skill treats Step 5 as a no-op if `zotero.notebooklm` is missing from config. For local RAG, the integration point is after Step 4 (save) and before Step 6 (build). A typical local RAG setup would:

1. Point a [LlamaIndex](https://www.llamaindex.ai/) or similar ingest job at `raw/`
2. Rebuild the index on a cron, or after each pipeline run via a post-save hook
3. Query it from your own chat interface

The pipeline does not bundle this — it's a separate tool in your stack. The only thing Research Hub needs to know is "don't try to open Chrome".

---

## Disabling steps you don't want

Planned for Phase 2 but not yet in v0.2.1: an env var `RESEARCH_HUB_SKIP_STEPS=5,7` to skip specific pipeline steps without editing code.

Until then, the simplest way to disable Step 5 is to leave `zotero.notebooklm` unset in config (the skill checks for it). To disable Step 7 (citation graph exploration), answer "no" when the skill asks "add these related papers?" at the end of a run.

---

## When to change source code

You should only need to edit `src/` if you are:

- Adding a new data source to Step 2 (e.g. plugging in a different paper-search MCP server)
- Swapping the LLM classifier for a deterministic rule-based classifier (recommended for reproducibility)
- Adding a new Step 5 target beyond NotebookLM (e.g. local RAG, Notion, Roam)
- Changing the Obsidian note template format in `vault/builder.py`

For any of these, read the relevant module first, then open an issue before sending a PR.
