| name | research-radar |
| description | Weekly scan of arXiv for papers relevant to the work in this repo — derive themes from the repo, query arXiv, curate the few most relevant papers, write a dated digest, and open a PR. Use when the user asks to "run the research radar", "scan arxiv", "find recent papers relevant to <repo>", "what should we read this week", or when invoked by the weekly scheduled remote agent. |
Research radar
This skill produces a weekly, curated digest of recent arXiv papers relevant to what this repo is
actually building, and ships it as a dated file under config.paths.researchRadarDir
(YYYY-MM-DD.md) in a PR. Where the changelog skills record what shipped, this one records what's
worth reading.
Two things make it work, and both are leaned on hard:
- The repo defines "our work." Because this runs inside the repo with
git/gh, it doesn't
need a hand-maintained interest profile — it derives current themes from recent PRs, active
planning docs, and the source (or uses an explicit theme list from config). The filter is the
entire value of the skill; a generic "AI papers this week" digest is worthless.
- The committed reports are the dedup memory. Every run leaves a file in
config.paths.researchRadarDir. Each run reads the recent ones and skips papers already
surfaced, so a paper appears at most once even as it lingers near the top of arXiv for weeks.
Audience: the people building this repo. Every paper earns its place with a concrete "why this
matters to us" — a subsystem it informs, a problem we're fighting, an open issue it speaks to. Not
"this is an interesting paper about agents."
Load the repo config
Before anything else, load the repo config (see
../../../core/reference/config-schema.md):
-
Read .claude/maintainerd.json from the repo root.
-
If it does not exist, STOP and tell the user:
This repo has no .claude/maintainerd.json. Run /bootstrap to generate it, then re-run me.
Do not guess values or hardcode another repo's settings.
-
Read the keys this skill needs:
config.repo — GitHub owner/name, passed to every gh ... --repo.
config.defaultBranch — the branch the PR targets (main, master, …) and that the repo is
PR-only against.
config.paths.researchRadarDir — where this skill writes YYYY-MM-DD.md. If absent, fall back
to planning/research-radar/ and note the fallback in your report.
config.researchRadar.themes — "derive" (infer from the repo) or an explicit string array.
If the whole researchRadar section is absent, default to "derive" and note it.
config.researchRadar.userAgent — the courtesy User-Agent for the arXiv API. If absent, fall
back to research-radar/1.0 (mailto:maintainer@example.com) and note that the user should set a
real contact.
- For theme derivation:
config.paths.designDocs and config.paths.productDocs (doc roots) and
config.paths.source (source root).
Themes (the query spine)
The set of research themes is the stable spine of the search. It comes from
config.researchRadar.themes:
- Explicit array — use those themes verbatim as the spine. Still sharpen them with fresh repo
signal each run (step 1), but don't invent themes outside the list.
"derive" — infer the themes from the repo itself (step 1). Build the spine from what the repo
is actually working on: recent merged PRs, the design/product docs, and the source. Do not assume a
domain — let the repo's own signal define the interest profile.
arXiv categories to scope to: pick the categories that match this repo's domain so the scan surfaces
signal without flooding. For an LLM-agent / AI codebase, the set that surfaced signal without
flooding in testing is cs.AI, cs.CL, cs.HC, cs.MA. cs.LG is a firehose of
training/architecture papers that mostly adds noise; add cs.LG, cs.IR, or cs.SE to a query only
when a week's repo signal is specifically about learning internals, retrieval, or coding agents.
Relevant work cross-listed from those categories already surfaces via the defaults. A repo in a
different domain should choose the arXiv categories that match its themes instead.
Workflow
1. Establish "our work" — derive or refresh the themes
Pull recent signal so the scan tracks what we're working on now. If config.researchRadar.themes
is "derive", this step is the theme source; if it's an explicit array, this step sharpens it.
gh pr list --repo <config.repo> --state merged --limit 50 --json number,title,mergedAt \
--jq '.[] | "\(.mergedAt[:10]) #\(.number) \(.title)"'
git log --since="3 weeks ago" --name-only --pretty=format: -- <config.paths.designDocs> | sort -u | sed '/^$/d'
Also skim the source under config.paths.source and the docs under config.paths.productDocs (plus
a memory index like MEMORY.md / CLAUDE.md / AGENTS.md if present) for ongoing project themes.
Fold anything notable — a new subsystem, a hard problem we're chewing on — into the keyword set for
step 2. Example: a week heavy on voice barge-in work → add "endpointing", "turn-taking",
"full-duplex" to the queries.
2. Query arXiv
Use the arXiv API (open, no key). Build one or a few search_querys combining the categories with
the keyword spine, sorted newest-first. URL-encode spaces as +, parens as %28/%29, and quote
phrases.
mkdir -p /tmp/radar
UA="<config.researchRadar.userAgent>"
curl -sS -m 30 -A "$UA" 'https://export.arxiv.org/api/query?search_query=%28cat:cs.AI+OR+cat:cs.CL+OR+cat:cs.HC+OR+cat:cs.MA%29+AND+%28abs:%22language+model+agent%22+OR+abs:%22tool+use%22+OR+abs:%22agent+memory%22+OR+abs:%22multi-agent%22%29&sortBy=submittedDate&sortOrder=descending&max_results=150' \
> /tmp/radar/q1.atom
(Swap the cat: set and the abs: keyword phrases for this repo's categories and theme spine — the
example above is an AI/agent profile.)
https:// and the -A User-Agent are both load-bearing — arXiv returns an empty body for
plain-http or UA-less requests, which would silently look like a quiet week. Keep them. Run a
second query (q2.atom, …) for a different theme cluster if one query's keywords get unwieldy.
Sleep ~3s between calls — arXiv asks for ~1 request / 3 seconds; a weekly job is otherwise
trivially within budget.
Parse the Atom into a compact list and filter to the last 7 days by submitted date
(<published>). Filtering by submitted (not <updated>) is deliberate: it keeps v2/v3 revisions of
old papers from re-surfacing.
python3 - /tmp/radar/*.atom <<'PY'
import sys, json, re, datetime, xml.etree.ElementTree as ET
A, X = "{http://www.w3.org/2005/Atom}", "{http://arxiv.org/schemas/atom}"
cutoff = datetime.date.today() - datetime.timedelta(days=7)
seen, out = set(), []
for path in sys.argv[1:]:
for e in ET.parse(path).getroot().findall(f"{A}entry"):
pub = (e.findtext(f"{A}published") or "")[:10]
if not pub or datetime.date.fromisoformat(pub) < cutoff:
continue
url = (e.findtext(f"{A}id") or "").strip()
base = re.sub(r"v\d+$", "", url)
if base in seen:
continue
seen.add(base)
pc = e.find(f"{X}primary_category")
out.append({
"url": url,
"title": " ".join((e.findtext(f"{A}title") or "").split()),
"abstract": " ".join((e.findtext(f"{A}summary") or "").split()),
"published": pub,
"primary": pc.get("term") if pc is not None else "",
"authors": [a.findtext(f"{A}name") for a in e.findall(f"{A}author")][:6],
})
print(json.dumps(out, indent=2))
print(f"\n# {len(out)} papers in window", file=sys.stderr)
PY
If the newest query returned max_results entries and the oldest is still inside the window, you
truncated — raise max_results and re-fetch. If curl fails or returns zero <entry> elements (a
network/API problem, distinct from a genuinely quiet week), retry once after a few seconds; if it
still fails, stop — do not open a PR and report the failure (see "What not to do").
3. Curate and judge
This is the work. From the in-window list, select the ~5–8 papers most relevant to what we're
building — the themes sharpened by step 1's signal. Read each candidate's abstract and ask: does
this inform a subsystem of this repo, a problem we're actively fighting, or an open issue? Rank by
that, not by general interest. A strong week might surface 10; a thin one, 2. Zero is possible but
rare — if so, see the quiet-week path in step 5.
4. Drop papers we've already surfaced
ls -1 <config.paths.researchRadarDir> 2>/dev/null | tail -8
Read the most recent several reports and collect the arXiv IDs they list. Drop any candidate whose
version-stripped ID already appears. The committed history is the dedup ledger — this is why every
run commits a file.
5. Write the report
Path: config.paths.researchRadarDir/YYYY-MM-DD.md (the run date). Create the directory if
missing. Template:
# Research radar — <Month DD, YYYY>
**Window:** <YYYY-MM-DD> – <YYYY-MM-DD> · **Surfaced:** <N> of <M> scanned
**Queried:** <categories queried this run, e.g. cs.AI, cs.CL, cs.HC, cs.MA>
---
## This week
<2–4 sentences: the through-line. What's most relevant to what we're building right now; call out any cluster ("three papers on agent memory this week"). If thin, say so plainly.>
## Papers
### [<Title>](<abs url>)
<first author et al.> · <primary category> · submitted <YYYY-MM-DD>
<1–3 sentences: what the paper does, then concretely why it matters to this repo — name the subsystem or open issue where the link is real. If the link is a stretch, move it to "Also noted" instead of overselling it.>
### ...
## Also noted
<Optional one-liners for borderline/adjacent papers not worth a full card — title link + half a sentence. Omit the section if empty.>
Quiet-week path: if step 3 genuinely found nothing relevant and the fetch succeeded, still
write the file with a one-line "This week" (e.g. "Quiet week — nothing in-window met the bar; scanned
.") and an empty Papers section. The file keeps the cadence and the dedup ledger continuous, and
the PR is honest about being a no-content week.
6. Open the PR
The repo is PR-only — never push to config.defaultBranch.
If create-pr is installed, delegate the branch/commit/PR mechanics to it after writing the
file: it runs the repo's pre-flight gates and enforces the PR template. Tell it to branch from
config.defaultBranch with a research-radar-$(date +%Y-%m-%d) branch name and use the body
described below.
Otherwise, open it inline:
git checkout <config.defaultBranch> && git pull
git checkout -b research-radar-$(date +%Y-%m-%d)
git add <config.paths.researchRadarDir>
git commit -m "Research radar — $(date +%Y-%m-%d) (<N> papers)"
git push -u origin HEAD
gh pr create --repo <config.repo> --base <config.defaultBranch> \
--title "Research radar — $(date +%Y-%m-%d)" --body "<see below>"
PR body = the "This week" synthesis, then a bullet list of the surfaced papers as
- [Title](url) — one-clause why, so the digest is reviewable from the PR without opening the file.
Reply to the caller with the PR URL and a one-line shape ("6 papers, heavy on agent memory"). Don't
paste the whole report back.
Voice and style
Match the repo's planning/doc voice — prose-forward, explains the why, no shouting.
- No emojis. No marketing language. State what a paper does and why it's relevant.
- Link every paper to its arXiv abstract page, always. The paper is the source of truth.
- "Why it matters" is builder-facing and concrete. Tie to a subsystem, a problem, or an issue
number where the link is real. "Interesting work on agents" is not a reason.
- Be honest about relevance. A tangential paper goes under "Also noted" or gets cut. Don't pad
the main list to hit a number.
- Report only the abstract's claims. You read abstracts, not full papers — frame accordingly
("the abstract reports…") and never assert results you didn't read.
What not to do
- Don't fabricate papers, authors, titles, or results. Only report
<entry> items the API
actually returned. A hallucinated paper in a research digest is the worst possible failure here —
when in doubt, drop it.
- Don't open a PR when the fetch failed. Distinguish a genuinely quiet week (fetch OK, nothing
relevant) from a broken fetch (network/API error). The first writes a quiet-week file; the second
stops and reports the error. Never label a failure as "quiet."
- Don't re-surface papers from prior reports. The dedup step is load-bearing; skipping it makes
the digest repeat itself week over week.
- Don't dump the scan. 5–8 curated papers, not 150. The synthesis and selection are the
deliverable.
- Don't push to
config.defaultBranch. Branch + PR, every time.
- Don't widen the window to pad a thin week. Seven days, by submitted date. A quiet week is
honest signal.
- Don't reuse a branch from a prior run. Same-day re-runs append a suffix.
Scheduling
Runs in its own weekly /schedule slot (e.g. Monday morning). It is intentionally not folded into
daily-update — that bundles per-day skills, and this is weekly. If more weekly skills appear
later, mint a weekly-update meta-skill on the daily-update pattern and move this behind it.
Model tier: deriving themes and curating relevance is judgment — schedule on capable (or a
mid rung if defined); the arXiv fetch/parse is cheap but can't be split from the same run. See
../../../core/reference/model-tiers.md.
Related skills
bootstrap — generates the .claude/maintainerd.json this skill reads.
create-pr — opens the PR (runs the repo's gates, enforces the PR template) once the digest is
written; this skill delegates step 6 to it when installed.
daily-update — the per-day meta-skill; deliberately separate from this weekly job.
audit-architecture — the other repo-derived scanner; it sweeps the code for tech debt where this
one sweeps arXiv for reading.