Status: implemented (designed 2026-07-18) · Skill: literature
Context
Theliterature skill’s scout and position modes both walk one citation-graph
substrate: OpenAlex (keyless backbone, mailto= polite pool) plus Semantic
Scholar (S2AG — citation contexts, SciCite intents, isInfluential,
recommendations). Today there is no client: ../../../skills/literature/SKILL.md
(§ Tooling) marks it unimplemented. Interim (until the module is implemented): the skill orchestrates
the graph step manually / via direct tool calls — persisting raw JSON as the
provenance root and editing references.json / triage.yml — which is
unreproducible and pushes pagination, degradation, and rate-limit handling onto the
operator. Once honest-scholar is installed (via
ensure-tooling) the skill calls
honest-scholar literature … instead.
This proposes the first cut of honest_scholar/literature/graph.py (exposed as
honest-scholar literature …): the read/enrich half of the
substrate (graph traversal + enrichment + neighbor sets). The registry
loader/appender, triage join, PRISMA-log and concept-matrix generators are noted as
out of scope here (separate proposal) so this stays focused.
Goal
A light-dependency, JSON-emitting CLI + importable functions that givescout
(steps 1–5 of SKILL.md) and position (snowball backward+forward) everything they
need from the graph: resolve any id to a canonical work, page forward citations and
backward references, enrich with the fields ranking turns on, and compute
co-citation / bibliographic-coupling neighbor sets. Degrade to OpenAlex-only when no
S2 key is present. Reproducible: same id + filters + search date → same set.
Design sketch
CLI (honest-scholar literature <cmd> [args] --json), each command prints one
JSON document to stdout; raw upstream responses are written to a --provenance-dir
as the provenance root before any enrichment. The same functions are importable
from honest_scholar.literature.graph.
Identifier resolution.
resolve() normalizes DOI / arXiv: / OpenAlex W… /
S2 (CorpusId: / SHA) to a canonical record {openalex, doi, s2, arxiv, title, year}. OpenAlex is the identity anchor (its work id keys everything); S2 ids are
attached when available. Ambiguous/missing → record with resolved: false + reason,
never a crash.
Forward citations — OpenAlex works?filter=cites:<W>&mailto=…&per-page=200,
paginated via cursor=* until exhausted or --max. Each row carries provenance
{source_id, via: "openalex"}.
Backward references — read referenced_works[] off the anchor’s OpenAlex record
(one call), then batch-hydrate to metadata.
Enrich — merge into a stable shape per work:
{id{openalex,doi,s2,arxiv}, title, year, venue, cited_by_count, authors[], abstract, context_snippet, intent, is_influential}. year/venue/counts/authors/ abstract from OpenAlex (abstract reconstructed from its inverted index);
context_snippet (exact citing sentence), intent (SciCite:
Background/Method/Result-comparison), is_influential from S2
/paper/{id}/citations?fields=contexts,intents,isInfluential,…. Fields absent under
OpenAlex-only degrade to null with a top-level degraded: ["context","intent"]
marker — the caller records it in run provenance.
Neighbor sets (expose sub-fronts, SKILL.md scout step 5):
- co-citation — rank works co-cited with the anchor (forward sets intersected / counted), score = shared citing papers.
- bibliographic coupling — rank works sharing references with the anchor
(
referenced_worksoverlap), score = shared references (works for brand-new papers). Output:[{id, score, title, year}], truncated to--top. Pure set arithmetic over already-fetched OpenAlex data — no extra API surface.
recommendations/v1/papers/forpaper/<id> when an S2 key is present; behind a
recommend subcommand, omitted here if it complicates v1.
Dependencies & posture
Light-dep, per SKILL.md and../../../resources/substrate/asset-registry.md:
- HTTP:
requests— the house HTTP client forhonest-scholar(decided intooling-package.md; already pulled bypooch, so zero net dep). No OpenAlex/S2 SDKs, no graph libraries (networkxetc.); neighbor math is stdlibset/collections.Counter. - stdlib for JSON and the on-disk cache; the CLI layer is Typer (the
package’s house CLI framework, per
tooling-package.md), not argparse. mailto=on every OpenAlex call (polite pool);x-api-keyheader on S2 iff a key is configured (env /.honest-scholar/config.yml), else omit and degrade.- Rate-limits / caching: respect OpenAlex 10 req/s + 100k/day and S2’s lower keyless ceiling; exponential backoff on 429; a content-addressed on-disk response cache (keyed by URL) so re-runs are cheap and reproducible. Cache is the provenance root — never silently refreshed within a run.
- No PDF handling: bytes stay under the substrate’s resolution chain + license gate.
Open questions
- Abstract reconstruction from OpenAlex inverted index — always, or opt-in (size)?
- Neighbor scoring for large anchors (10k+ citers): cap the frontier, or stream +
approximate? What
--topdefault? - S2 batch endpoint (
/paper/batch) vs. per-id calls for hydration — batch cuts request count but couples to a second response shape. - Where does the cache live — gitignored under
.honest-scholar/cache/shared with the substrate cache, or a private dir? TTL / invalidation policy? - Does
resolveneed Crossref as a DOI fallback when OpenAlex misses, or is OpenAlex-only acceptable for v1?
Acceptance criteria
-
resolvemaps DOI / arXiv / OpenAlex / S2 ids to one canonical record; misses returnresolved: false+ reason, no crash. -
citespaginates OpenAlex via cursor to completion (or--max);refsreturns hydratedreferenced_works. -
enrichemits the full field bundle;context_snippet+intent+is_influentialpopulate when an S2 key is present. - With no S2 key, all commands succeed OpenAlex-only and set
degraded: […]; no command hard-requires S2. -
neighborsreturns co-citation and bibliographic-coupling sets with scores. - Every command writes raw upstream JSON to
--provenance-dirbefore enriching;mailto=always sent; runs are cache-backed and reproducible. - 429/backoff handled; rate ceilings respected.
- Deps limited to
requests+ stdlib (+pyyamlfor config read); no graph/SDK libraries.
Links
- Skill:
../../../skills/literature/SKILL.md(§ Tooling) - Scout methodology:
../../../resources/references/citation-scouting.md - Position methodology:
../../../resources/references/related-works-synthesis.md - Substrate + light-dep posture:
../../../resources/substrate/asset-registry.md - Skill design:
../02-literature.md· Substrate contract:../04-substrate-and-contract.md - Registry format: ADR-0008 (CSL-JSON), ADR-0020 (triage sidecar)