Literature Sync: Zotero + Obsidian Pipeline
Takes the .bib output of /search-lit (or any user-specified .bib file) and
synchronizes the references into the Zotero library and Obsidian literature notes.
When enough literature notes accumulate, extracts cross-cutting concept notes.
Communication Rules
- Communicate with the user in their preferred language (typically Korean).
- Note template headings (
서지 정보,핵심 내용,내 생각,관련 노트), vault folder paths (02 연구/문헌/,02 연구/개념노트/), and Obsidian-side conventions are preserved as literal template content because they match the user's existing vault.
When to Use
- After
/search-litcompletes — sync the produced .bib into Zotero + Obsidian. - Bulk-register references from an existing .bib into Zotero + Obsidian.
- Tidy the
references/folder inside a project workspace. - On explicit concept-extraction request → extract cross-cutting concepts from existing literature notes.
Prerequisites
- Project owner only —
/lit-syncis an owner-scoped operation perdocs/zotero_policy.md. Collaborators consume the committedmanuscript/_src/refs.bibsnapshot read-only. - Zotero desktop 7.x + Better BibTeX plugin installed.
- Better BibTeX "Keep updated" auto-export configured to
<project>/manuscript/_src/refs.bib(owner setup checklist indocs/zotero_policy.md§Setup). - Zotero MCP server available (skip the Zotero phase if not connected; auto-export refresh still fires once Zotero is reopened).
- Obsidian CLI or direct file writing to the Obsidian vault.
- Obsidian vault path: configured in user's environment (e.g.,
$OBSIDIAN_VAULT).
Artifact Contract
Per docs/artifact_contract.md, /lit-sync is the sole writer of:
| Artifact | Writer | Readers |
|---|---|---|
manuscript/_src/refs.bib | /lit-sync (via Better BibTeX auto-export trigger) | /write-paper, /verify-refs, /render |
references/zotero_collection.json | /lit-sync | /verify-refs, /sync-submission |
Direct hand edits to refs.bib are drift — revert on sight.
Pipeline Overview
.bib file (or /search-lit output)
│
▼ Phase 1: Parse
Extract DOI, PMID, title, authors, journal, year
│
▼ Phase 2: Zotero Sync (owner)
Dedupe → zotero_add_by_doi → place in collection → pin citekey
│
▼ Phase 2.5: refs.bib snapshot refresh
Trigger Better BibTeX auto-export → verify manuscript/_src/refs.bib mtime updated
│
▼ Phase 3: Obsidian Literature Notes
Create 02 연구/문헌/{citekey}.md (empty note OK — fill later with highlights)
│
▼ Phase 4: Concept Extraction (conditional)
≥10 literature notes → scan for cross-cutting concepts → propose concept notes
Phase 1: Parse BibTeX
Input
The user-specified .bib file path, or the .bib just produced by /search-lit.
Process
# Parse .bib entries with regex.
# Extract per entry:
# - citekey (e.g., Kim_2024_Validation)
# - doi
# - pmid
# - title
# - authors (first + last minimum)
# - journal
# - year
# - volume, number, pages (if present)
Log any parse failures and skip those entries.
Phase 2: Zotero Sync
Step 2.1: Determine project collection
Identify the project from the current working directory or from an explicit user override. Reuse an existing collection key if one is recorded; otherwise create a new collection.
Collection mapping: Check existing Zotero collections for the current project.
If no collection exists, create one with zotero_create_collection. Record the
collection key for future use.
Step 2.2: Dedupe + add
For each entry:
- Use
zotero_search_itemsto search by DOI or title — if already present, skip. - Otherwise call
zotero_add_by_doi(when a DOI is available) orzotero_add_by_url(falling back to the PubMed URL when no DOI is available). - Use
zotero_manage_collectionsto place the item in the project collection.
Step 2.3: Result report
Zotero Sync:
Added: 8 papers (new)
Skipped: 3 papers (already in library)
Failed: 1 paper (no DOI/PMID)
Collection: RFA-Meta (TZQEP4NH)
If the Zotero MCP is not connected, skip this entire phase and proceed to Phase 3.
Always write references/zotero_collection.json in the project workspace:
{
"schema_version": 1,
"status": "synced",
"collection": "RFA-Meta",
"collection_key": "TZQEP4NH",
"added": 8,
"skipped": 3,
"failed": 1
}
If Zotero is unavailable, write the same file with status: "skipped" and a
human-readable reason.
Phase 2.5: refs.bib snapshot refresh
Better BibTeX "Keep updated" auto-export normally refreshes manuscript/_src/refs.bib within seconds of a Zotero change. This phase verifies the snapshot actually updated before downstream skills consume it.
Step 2.5.1: Resolve path
Read SSOT.yaml → truth.refs_bib. Default: manuscript/_src/refs.bib. If absent (legacy project), fall back to manuscript/_src/refs.bib and emit a WARN recommending SSOT migration.
Step 2.5.1b: Precondition assertion (early-exit, do NOT poll)
Before entering the 10s polling loop in Step 2.5.2, verify both preconditions. If either fails, abort Phase 2.5 with setup instructions instead of waiting for a timeout that will never resolve.
-
BBT auto-export registered.
~/Zotero/better-bibtex/read-only.jsonmust be a non-empty JSON list. Check with:python3 -c 'import json,sys,pathlib; p=pathlib.Path.home()/".zotero"/"zotero"/"Profiles"; \ f=pathlib.Path.home()/"Zotero"/"better-bibtex"/"read-only.json"; \ sys.exit(0 if f.exists() and json.loads(f.read_text() or "[]") else 1)'Or equivalent shell:
[ -s ~/Zotero/better-bibtex/read-only.json ] && [ "$(jq 'length' ~/Zotero/better-bibtex/read-only.json)" -gt 0 ].On failure print:
Phase 2.5 skipped: BBT auto-export not configured (
~/Zotero/better-bibtex/read-only.jsonis empty or missing). Set up "Keep updated" auto-export perdocs/zotero_policy.md§Setup, then re-run/lit-sync. -
Target refs.bib exists. The resolved
truth.refs_bibpath from Step 2.5.1 must exist on disk (even empty is OK — BBT will overwrite). On failure print:Phase 2.5 skipped: target snapshot
<path>not found. Configure BBT auto-export with "On Change" to the SSOT path, then re-run.
In either early-exit, set refs_bib_refreshed: false + reason: "precondition:<which>" in the Step 2.5.3 JSON and return control to the caller. /verify-refs treats refs_bib_refreshed: false as an unverified snapshot — downstream skills (/write-paper, /render) block until the precondition is resolved.
Rationale (2026-04-24 Phase 1B-b dry-run): on a machine with BBT installed but no auto-export registered, the original Step 2.5.2 polled for 10s then emitted a generic "mtime unchanged" WARN that did not point at the actual cause. Findings: ~/.local/cache/phase1b_b_dryrun/findings.md.
Step 2.5.2: Verify refresh
After Phase 2 adds items:
- Capture
stat -f "%m" manuscript/_src/refs.bibbefore Zotero writes. - Wait up to 10s (Better BibTeX debounce). Poll mtime.
- If mtime unchanged after 10s:
- Prompt user to check Zotero is running and BBT export is "Keep updated".
- If BBT auto-export path is wrong, print the expected path (
<project>/manuscript/_src/refs.bib) and refer todocs/zotero_policy.md§Setup. - As last resort, offer manual export:
File → Export Library → Better BibTeX → target path.
- Once mtime advances, grep for the newly added citekeys. All must be present; if any is missing, report as failure (do NOT fabricate entries).
Step 2.5.3: Record in zotero_collection.json
Append to the JSON written in Step 2.3:
{
"refs_bib_path": "manuscript/_src/refs.bib",
"refs_bib_mtime": "2026-04-24T14:32:11Z",
"refs_bib_refreshed": true,
"citekeys_verified": ["Kim_2024_Validation", "..."]
}
If refresh failed, set refs_bib_refreshed: false and i