name: paper-summarize
description: After research-hub ingests a cluster of cited papers, fill the per-paper Key Findings + Methodology + Relevance sections in BOTH Obsidian markdown and the Zotero child note. Use when the user says "fill the TODO Key Findings/Methodology blocks left by research-hub auto", "I just ran auto and don't know what these papers are about", or "summarize the papers in cluster X". Invokes a supported LLM CLI on each paper's abstract. NOT for summarizing the user's own manuscript draft — that's paper-memory-builder. NOT for cluster-level briefs — that's research-hub notebooklm generate. This skill is per-cited-paper only. If the user says "extract claims from these papers", disambiguate before acting: their own manuscript draft → paper-memory-builder; a cross-paper comparison matrix → literature-triage-matrix; per-cited-paper Key Findings → this skill.
paper-summarize
The auto pipeline ingests metadata + abstract only — Summary / Key Findings / Methodology / Relevance stay as [TODO] skeletons in both Obsidian and Zotero. Cluster-level summarization (NotebookLM brief, crystals) does NOT fill per-paper notes. So after auto, the user has nothing scannable per paper without opening the PDF.
This skill fills that gap. One LLM call per paper, JSON-validated, written to both vault systems atomically (rollback the markdown change if Zotero write fails so the two stay in sync).
When to use
Trigger phrases:
- "Summarize the papers in cluster X."
- "Fill the TODO Key Findings for X."
- "I just ran
auto X and the notes are empty — give me real summaries."
- "Update Zotero notes for cluster X with what each paper actually says."
Not for:
- Generating a single CLUSTER-LEVEL summary — that's
research-hub notebooklm generate (NotebookLM brief).
- Filling Q&A on the cluster — that's
research-hub crystal emit/apply.
- Reading PDFs — abstract-only by design. PDF parsing is out of scope; the LLM is told to mark "[PDF needed]" if abstract is too thin.
- Verifying brief vs source — that's
notebooklm-brief-verifier.
Inputs
- Cluster slug (must already exist in the vault under
raw/<slug>/)
- Optional LLM CLI override (
claude, codex, gemini, opencode, aichat, cursor, or configured custom adapter)
- Optional
--apply flag (default off, dry-run)
The skill reads each paper's frontmatter (DOI, year, zotero-key) + the ## Abstract body block. Papers with empty abstract get a "PDF needed" marker rather than hallucinated findings.
Outputs
For each paper, three sections are rewritten:
-
Obsidian markdown at raw/<cluster>/<paper-slug>.md:
## Key Findings callout block (3–5 bullets)
## Methodology callout block (one sentence)
## Relevance callout block (1–2 sentences linking to the cluster topic)
- Anchor IDs (
^findings, ^methodology, ^relevance) preserved.
-
Zotero child note for the paper's parent item (looked up via zotero-key frontmatter):
- Existing note HTML overwritten with
<h1>Summary + Abstract + Key Findings <ul> + Methodology + Relevance.
- If no child note exists, one is created.
Both writes must succeed for a paper to count as "applied". A Zotero write failure rolls back the markdown change so the two systems stay in sync.
Failure modes
- No LLM CLI on PATH: prompt is saved to
<vault>/.research_hub/artifacts/<cluster>/summarize-prompt.md. The user pipes it through their LLM manually and re-runs with --apply, or calls the apply_cluster_summaries MCP tool with the parsed payload. Exit code 0; ok=True.
- LLM returns malformed JSON: report is
ok=False with error="LLM response had no parseable JSON object". No writes.
- Paper slug in payload not in cluster: that entry is skipped with reason logged; other entries still applied.
- Empty key_findings / methodology / relevance: entry skipped.
- Zotero write fails for one paper: that paper's markdown change is rolled back; other papers still applied.
Verification
This skill's contract is exercised end-to-end by the test suite. The
suites stub the LLM CLI but execute real Obsidian markdown writes against
a fixture vault and real Zotero child-note writes against a mocked Zotero
adapter. Rollback semantics (markdown rolls back if Zotero write fails)
are explicitly tested.
python -m pytest -q \
tests/test_v069_summarize.py \
tests/test_v073_parallel_summarize.py \
tests/test_v080_resummarize.py
Coverage breakdown:
| Test file | Count | What it covers |
|---|
tests/test_v069_summarize.py | 17 | Prompt builder shape, validator (rejects unknown slugs / empty fields / wrong types), Obsidian + Zotero apply path, rollback when Zotero fails, no-LLM-on-PATH fallback |
tests/test_v073_parallel_summarize.py | 3 | Parallel summarize across multiple clusters, ordering, error isolation |
tests/test_v080_resummarize.py | 3 | Re-summarize behaviour: idempotent on same input, overwrite on different LLM output, --no-zotero path |
For a manual smoke check against a live vault:
research-hub summarize --cluster <slug>
research-hub summarize --cluster <slug> --apply
research-hub summarize --cluster <slug> --llm-cli codex --apply
research-hub summarize --cluster <slug> --apply --no-zotero
After applying, scan raw/<cluster>/*.md — every paper's ## Key Findings callout should have real bullets instead of [TODO].
Operating notes
- Findings-first: the prompt instructs the LLM to state the result, not "The paper shows that…".
- Anchored to the abstract: every Key Finding must trace to something the abstract claims. The LLM is told to mark uncertain claims with
[likely].
- Idempotent: re-running on a cluster with already-filled findings will overwrite them with whatever the LLM produces this run. Use
--no-zotero or --no-obsidian if you want to preview one side first.
- Same dual-write discipline as the abstract-sync helpers in
.scratch/: one transactional unit per paper.