Effort: deep
Researchers spend years curating Zotero libraries that represent
their personal map of a literature. Those libraries are exactly the
"trusted seed corpus" SciDEX needs to bootstrap a researcher-
specific gap-scanner or literature review. Wire Zotero's Web API +
the pyzotero skill (already shipped in the K-Dense skills
bundle) so a logged-in researcher (via q-integ-orcid-oauth-claim)
can import a Zotero collection as a SciDEX evidence corpus, with
items materialised as papers rows and the collection itself
becoming a queryable artifact.
migrations/<date>_zotero_corpora.sql:CREATE TABLE zotero_corpora (
id UUID PRIMARY KEY,
researcher_id UUID NOT NULL REFERENCES
researcher_identity(id),
zotero_user_id TEXT NOT NULL,
zotero_collection_key TEXT NOT NULL,
collection_name TEXT,
item_count INT,
last_synced_at TIMESTAMP DEFAULT NOW(),
sync_status TEXT NOT NULL DEFAULT 'pending' CHECK
(sync_status IN ('pending','syncing','synced','error')),
last_error TEXT,
UNIQUE(researcher_id, zotero_collection_key)
);
CREATE TABLE zotero_corpus_items (
corpus_id UUID NOT NULL REFERENCES zotero_corpora(id),
zotero_item_key TEXT NOT NULL,
paper_id TEXT REFERENCES papers(pmid),
doi TEXT,
title TEXT,
added_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (corpus_id, zotero_item_key)
);/researcher/zotero/connect accepts a Zotero API key + userzotero_corpora (key encrypted withsecrets_box).
scidex/atlas/zotero_import.py using thepyzotero skill:list_collections(researcher_id) — fetches all top-levelimport_collection(researcher_id, collection_key) —scidex/forge/tools.py), inserts/upserts papers rows,zotero_corpus_items.?corpus=<id> on/api/literature-search constrains results to the/api/gap-scanner seeds/researcher/{orcid_id} gains atests/test_zotero_import.py: stub pyzoteropyzotero skill (Anthropic skill already loaded intoq-integ-orcid-oauth-claim — supplies researcher_identity.q-perf-deferred-work-queue — runs the import.pyzotero skill (already in vendor/kdense-skills).q-integ-hypothesis-is-annotations — annotations created on aDelivered:
migrations/20260427_zotero_corpora.sql — zotero_corpora + zotero_corpus_itemsactor_id TEXT REFERENCES actors(id) instead ofresearcher_id REFERENCES researcher_identity(id) (that table not yet created byq-integ-orcid-oauth-claim). A migration can remap to researcher_identity later.scidex/atlas/zotero_import.py — full importer module:wallet_manager.py)store_credentials / list_collections / import_collection / _run_import@register("zotero_import_collection") deferred-queue handlercorpus_paper_ids / actor_corpus_contains_paper / list_corpora helpersapi.py:POST /api/researcher/zotero/connect — store encrypted credentialsGET /api/researcher/zotero/collections — list Zotero collectionsPOST /api/researcher/zotero/import — trigger deferred importGET /api/researcher/zotero/corpora — list imported corpora + statusGET /api/researcher/zotero/corpora/{id}/status — poll one corpusGET /api/papers/search?corpus=<id>&q=<query> — corpus-scoped paper searchGET /api/gaps/corpus-seeded?corpus=<id> — gaps scoped to corpus papersGET /researcher/zotero — connection UI pagetests/test_zotero_import.py — 11 tests: encryption round-trip, list_collectionsDeviations from spec:
researcher_identity → actors FK (upstream dependency not yet created)paper_cache.