| name | academic-search |
| description | Query the keyless academic and public-domain registries (OpenAlex, Crossref, Semantic Scholar, Project Gutenberg, Internet Archive, Stack Exchange) for a topic and get back normalized, copyright-safe reference metadata (title, link, provenance). Use when gather-citations or verify-claims needs primary-literature grounding instead of the LLM's training memory. |
| metadata | {"agent":"research-factcheck","source":"Project Atlas Agent Skills Manifest §4 (academic grounding)","layer":"L5-content-verify"} |
academic-search
Retrieve source-linked references from the keyless scholarly / public-domain registries, governed
by the provider-policies cache + rate limit on the NestJS side.
When to use
- gather-citations needs a factual spine from primary literature (dossier grounding).
- verify-claims needs to check a draft's dates/names/causal claims against real scholarship.
- A "Hidden Legend" needs the 19th-century books or modern papers that document them.
Sources (server/src/modules/search/open-source-search.service.ts + AcademicSearchService)
- OpenAlex (CC0), Crossref (open bibliographic metadata) — routed through the polite pool via
the contact
mailto.
- Semantic Scholar (public tier) — title/year/authors/DOI only (no abstracts stored).
- Project Gutenberg (Gutendex) and Internet Archive — public-domain primary texts.
- Stack Exchange (History site by default) — attributable question metadata (title/link/score),
used to surface the human debates around a topic, not for trends.
All six are keyless. Retrieval is governed by AcademicSearchService: a policy-gated 30-day metadata
cache + per-source per-minute burst guard (server/src/modules/search/provider-policies/), so bulk
polling is cheap and polite automatically. Copyright-safe: only facts / reference metadata are
returned and stored, never article or book bodies (spec §2.5).
Function signature (manifest contract)
def academic_search(query: str, source: str | None = None) -> list:
"""Return normalized academic references [{source, title, url, meta}] for a query.
If `source` is given (openalex|crossref|semanticscholar|gutenberg|archive|stackexchange),
query only that registry; otherwise fan out across all six."""
Inputs / Outputs
- Input:
query (topic/subject/claim phrase); optional source for a targeted single-registry lookup.
- Output: list of
{source, title, url, meta} — meta is a short provenance string (year · author, etc.).
Implementation
Fan-out (all registries):
temporal-bridge → start_workflow("academicSearchWorkflow", "trend-signals", [{"query": query}]),
then poll get_workflow_result(workflow_id).
Single registry:
start_workflow("academicSearchSourceWorkflow", "trend-signals", [{"source": source, "query": query}]).
Runs the academicSearch / academicSearchSource Temporal activities
(server/src/modules/temporal/activities/academic-search.activities.ts) on the trend-signals
task queue, which go through the governed AcademicSearchService (cache + rate limit apply
automatically). This is the same reach pattern fetch-signals uses for SearchService.
Backend dependency
- No API keys required (all six registries are keyless). Redis powers the cache + burst limiter;
a Redis outage fails open (re-fetches), never blocks.
CONTACT_EMAIL (optional) sets the polite-pool mailto for OpenAlex/Crossref.
Model
No model call — this is deterministic retrieval + normalization. Feed the returned references into
gather-citations / verify-claims as grounding context.