CommunityResearch & Data Analysisgithub.com

source-triage

Finds sources on a topic and classifies each one before anything is read: legal access, which route an AI agent can actually read, integrity, and grade. Writes a ledger the other research skills use.

What is source-triage?

source-triage is a Claude Code agent skill that finds sources on a topic and classifies each one before anything is read: legal access, which route an AI agent can actually read, integrity, and grade. Writes a ledger the other research skills use.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Pushkar1-GitHub/D2D_Meta_Skills/tree/HEAD/skills/source-triage

Installed? Explore more Research & Data Analysis skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Source triage

Finds candidate sources, then answers four questions about each one before any analysis:

  1. Is it legally free? (access)
  2. Can I, an AI agent, actually read it, and by which route? (reach)
  3. Is it real, current and unretracted? (integrity)
  4. How good and how central is it? (grade, role, ⭐ context pick)

The output is a ledger. research-plan builds on it, evidence-check audits against it, and the user can use it on its own ("which of these can you read, which must I download?"). Works for any field and for non-paper sources.

Core truths (learned the hard way; do not relearn them)

  • Access is per route, not per paper. An OA article can be blocked on the publisher site and readable through an API, and the reverse. Record the route that worked.
  • HTTP 200 lies. CAPTCHA pages, login walls, JS shells, empty-body XML, and tools that report "success" with empty text all look like success. Check what you got.
  • WebFetch returns a model's extraction, not the page. Good for reading and for metadata; never a source of verbatim quotes. Verbatim text comes only from raw routes: API XML/JSON fetched with the shell (curl), a downloaded PDF, or a file the user gives.
  • Blocked ≠ missing. A page that blocks you leaves a source unchecked, never not_found. An API that is down means omit the signal, never record false.
  • Bronze ≠ readable. Test it.
  • Never circumvent. No shadow libraries, mirrors, caches, scraping round robots/CAPTCHA, or bulk harvesting.
  • Never name a source from memory. In trials, both models' first-draft references had wrong titles, wrong years, a wrong DOI and one paper that does not exist. Every source is looked up before it appears anywhere.

Write as you go (checkpoint)

Create research/<slug>/ledger.md at the start. After every search, append the query line to the search log. After classifying a source, append its row straight away. Long runs lose context; the ledger on disk is the memory.

Step 0 — Frame (one message, then go)

State in one line: question · field(s) · year range (default: last 10–15 yrs + landmark older work; "recent" = 5 yrs) · source types in scope · hard constraints from the request (e.g. "rubric needs 2 book chapters") · depth:

  • quick: keep 8–15 sources. Snowballing is optional (one forward snowball from the top review). Records beyond the cap are logged as not carried forward (cap): counted, but no exclusion reason needed.
  • deep: keep 25+, snowball backward and forward from the 3–5 most central works.

Proceed unless corrected. If the user said "check with me" / "step by step", stop at each ◆. Otherwise run straight through and list your choices at the end.

Step 1 — Library first

Check what the user already holds: a connected folder of PDFs, earlier research/*/ ledgers, a .bib or Zotero/CSL export. Owned sources are held: library. Pass their DOIs to the search as "already have".

Step 2 — Discover

Term set per concept, ORed together: synonyms, acronyms, older names, UK/US spellings, method names. For life science add MeSH + entry terms, gene symbols, enzyme names + EC numbers, Latin + common organism names. Query the method, not the author. Use 2–3 concept groups per query; more ANDs return 0 hits.

NeedPrimaryAlso
Biomedical / life sciencePubMed (MeSH)Europe PMC REST (SRC:"PPR" finds bioRxiv/medRxiv preprints; HAS_FT:y = full text available), OpenAlex
Any field, broadOpenAlex (topic/concept filters beat raw search)Semantic Scholar, Crossref
CS / ML / physics / quant-bioarXiv (sort by relevance)Semantic Scholar, alphaXiv / Scholar Feed
PreprintsEurope PMC SRC:"PPR" or OpenAlex type:preprintthe bioRxiv connector has no keyword search: use it only for a known DOI's details and published version
Book chaptersOpenAlex type:book-chapter, publisher catalogue, WebSearchverify the container (see Step 4)
Claims / reviews fastConsensus, Elicit (if connected)verify every hit by DOI
Non-paperthe owner's official docs/spec/code firstWebSearch for the rest
  • Sensitivity check: if key papers are known, confirm your queries find them.
  • Hit counts: tens of thousands of hits means too broad; 0–1 means over-constrained, so reword. Rewording a zero-hit query is part of the same round.
  • Record: title, authors, year, venue, type, DOI · PMID · PMCID · arXiv · URL, found_via.
  • Dedupe: DOI → other IDs → normalized title + year. Merge preprint + published version.
  • Bounds: ask before ~1,000 records or ~50 calls.

◆ Checkpoint: terms, queries, hit counts.

Step 3 — Access triage (before reading anything)

3a. Legal access (batch it)

For each kept source, call OpenAlex get_work (search results omit licence). Copy its oa_status value exactly: gold · diamond · hybrid · green · bronze · closed. Also record license and oa_url/pdf_url. Europe PMC license fields and PubMed get_copyright_status are fallbacks. Unpaywall (version, host_type) needs a real email: use only one the user approved for this; otherwise skip it.

  • No data → unknown. Never invent oa, ? suffixes or publisher names as access values.
  • Non-paper source → web.
  • Licence unknown → treat as local-only for redistribution.

3b. Agent reach: the route ladder (stop at the first success)

  1. Held: in the user's library or an earlier run.
  2. Europe PMC REST, raw, via shell curl where the shell can reach it:
    • search: …/rest/search?query=DOI:"a" OR DOI:"b"&format=json, ≤8 DOIs per call, ≥1 s apart;
    • full text: …/rest/{PMCID}/fullTextXML. This is the best verbatim route. Use WebFetch only if the shell can't reach it; WebFetch rejects long URLs and is rate-limited.
    • 429 → wait, retry once. 5xx → blocked:http-5xx, go to the next route.
  3. PubMed get_full_text_article: valid only if full_text is non-empty. Empty → blocked:empty-body.
  4. Preprint servers: arXiv, bioRxiv/medRxiv .full pages.
  5. OA locations: OpenAlex oa_url/pdf_url (a direct PDF URL may work where the article page fails), Semantic Scholar openAccessPdf, CORE, Crossref TDM links.
  6. Publisher page: one attempt per host. Following the DOI redirect is that attempt. Never follow into a robots-disallowed host (ScienceDirect / Elsevier linkinghub).
  7. Author or institutional repository via WebSearch "<exact title>" pdf. Respect "no text-and-data-mining / AI use" notices.
  8. Nothing worked: record the reason and route the source to the user.

Observed 2026-10 (re-test; these change):

  • Raw OK: Europe PMC REST via curl.
  • Extracted OK: Nature OA, Frontiers, MDPI, bioRxiv .full, arXiv, Springer PDF URLs from OpenAlex.
  • Blocked: ScienceDirect (robots), Wiley incl. OA (fetch error), Springer article pages, BMC PDF endpoint, PMC website (CAPTCHA), Annual Reviews, Science.
  • Unreliable: PubMed full-text tool (often empty), Europe PMC fullTextXML (HTTP 500 for some PMCIDs), Europe PMC via WebFetch (rate-limited).

Validate before calling anything readable:

  • body text beyond the abstract;
  • not a CAPTCHA / sign-in / access-denied page;
  • not empty or metadata-only XML.
  • A downloaded file counts only if it starts with %PDF-, is not HTML, and is over ~10 KB.
reachmeaning
readable:<route>full text obtained: readable:europepmc-xml (raw) · readable:pdf (raw) · readable:webfetch (extracted, not verbatim-capable)
abstract-onlyonly abstract obtainable (typical paywall)
blocked:<reason>paywall (not even an abstract) · robots · captcha · login · http-4xx · http-5xx · fetch-error · rate-limited · empty-body · js-shell
uncheckednot attempted (budget / cap). Never write "not tested".

Label rule: the controlled word comes first, exactly as in these tables. Notes may follow in parentheses, e.g. verified (Crossref DOI).

Look for a green copy before you label a source blocked or abstract-only. A source that is legally gold/hybrid/green/bronze but blocked for you goes in the "free but blocked for AI" section.

Step 4 — Integrity (batched; before naming anything)

Run this right after discovery for every source you will name: in the plan, as ⭐, or as a recommendation. It is not a final step.

  • Existence: verdicts verified · partial · mismatch · not_found · unchecked. Use Reference Checker check_references + OpenAlex resolve_references. When tools disagree, an exact DOI match in Crossref or OpenAlex beats a title-search not_found; record both.
  • Book chapters: verify the container, not just the DOI: book title, editors, pages and publisher from the Crossref record (api.crossref.org/works/<doi> → container-title, page). In trials a DOI was right but the book title was wrong.
  • Retraction: check_retractions. Clear means only "no notice found".
  • Peer review: label preprints (peer: preprint in the ledger; `preprint` on the sources-file entry); cite the published version if one exists.
  • Contamination: a 2024+ preprint that is unmatched in ≥2 indexes is a fabrication risk. Re-verify it or drop it.
  • Predatory venue flags; grey literature: URL + accessed date + Wayback link.

Step 5 — Grade, role, ⭐

Grade A–F: median of fitness for its own field, methodology, peer review, currency, data, conflicts. An integrity failure caps the grade at D. An unread or abstract-only source gets a provisional grade (B?). Role: evidence · background · method · tool/data · counter. Non-paper tiers: T1 owner docs/spec/code/statute · T2 institutional · T3 expert journalism · T4 community · T5 vendor marketing.

⭐ Context pack (5–10 picks): the sources that would most improve what is being built, if stored locally. Access is irrelevant. Each pick needs:

  • why it matters;
  • how to get it (OA link / VIT library login / interlibrary loan / email the author);
  • a Save-as name <firstauthor>-<year>-<keyword>.pdf;
  • redistribute: ok (CC-BY / CC0 / public domain) or local-only (everything else, including unknown licence).

Skew check: ≥70% of known values in one dimension → say so with the count.

Step 6 — The ledger

research/<slug>/ledger.md: the first table, one row per source, columns in this order: # · Short cite · Type · Role · Grade · access · licence · version · reach · held · read · integrity · retraction · peer · ⭐ · Save as · Link · found_via

  • read starts as not-read; research-plan fills in full · sections:<which> · abstract · metadata.
  • ⭐ = ok or local-only for picks, - otherwise.
  • Excluded rows use ids E1….

Below the table:

  • Search log: database · date · query · hits · which term found what.
  • Counts: identified → after dedupe → not carried forward (cap) → kept → excluded (with reasons). total in any Counts line = every ledger row, including excluded.

Run the bundled checker, python3 <this skill's folder>/scripts/research_checks.py ledger research/<slug>/ledger.md (standard-library Python, runs in your own sandbox), and fix errors before handing on. Used on its own, render the ledger as the sources file described in research-plan.

Privacy

Search with topic words only, never sentences from the user's unpublished work. The user's email goes only to Unpaywall, and only with their go-ahead.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Related Skills