Methodology · 方法论 · 方法論

This document is the contract between the repository and its readers. If the pipeline changes, this file changes in the same pull request.

1. Sources

Kind Endpoint Evidence tag Why it is trusted at this level
arXiv API https://export.arxiv.org/api/query, six cs.RO queries R primary author text, but self-reported and often not peer reviewed
arXiv category feeds https://rss.arxiv.org/rss/<category>, read only when the API returns nothing R the same papers, announced listings instead of search
Official vendor blogs AWS Physical AI, NVIDIA Technical Blog / NVIDIA Blog, Google DeepMind, Hugging Face O first-party statements about their own products
Lab research pages Toyota Research Institute R a lab’s own page for each paper: the same standing as the paper
Trade press IEEE Spectrum Robotics, The Robot Report; 雷峰网 Leiphone (Chinese), MONOist (Japanese) M professional reporting, still secondary
Standards watch ISO, EUR-Lex, Council of the EU O authoritative status and dates

Rules:

2. Classification

data/taxonomy.json defines eight lanes. Each lane has a keyword list and a weight. An item is matched case-insensitively against title + summary, as whole terms: a keyword counts only where it is not preceded or followed by a letter or digit, with an optional plural s / es. So ppo no longer fires inside “support”, thor inside “author” or droid inside “Android”. The lane with the highest hits × lane_weight wins. On a tie, any lane with hits beats foundation: nearly every robot-learning paper names the VLA or world model it builds on, and one that also names what it does with it belongs to that lane. Other ties resolve to the first lane in file order, which makes classification deterministic. Keywords and anchors include Chinese and Japanese terms for the zh and ja feeds; those items are quoted in their own language on all three pages. Their descriptions are cut at a third of the English length, which holds about as much text, so they do not collect three times the keywords. A keyword that is just as common outside its lane (“encoder”, “memory”, “standard”) is listed as a phrase (“rotary encoder”, “episodic memory”, “safety standard”).

A curated entry in data/baseline.json that declares a lane keeps it: the lane is an editorial decision, and the classifier is not allowed to overrule it. Such an entry is scored on its own lane’s keywords rather than the best-matching lane’s.

Before any lane counts, an item must be Physical AI at all. Items from a topical source (a "topical": true feed, or a paper listed in cs.RO) are on-topic by construction. Everything else, which means general company blogs and papers found only through other arXiv categories, must name at least one term from anchors in data/taxonomy.json: robot, humanoid, embodied, manipulation, VLA, world model, ROS 2, Isaac, LeRobot, Jetson and similar. Words like “agent”, “benchmark” or “inference” are lane keywords but not anchors, because a coding-agent post uses them just as much.

Signals are orthogonal flags: closed_loop, real_robot, open_release, latency, numbers. They are shown as badges and feed the score.

3. Scoring

score = min(lane_hits, 6) × 0.35 × lane_weight
      + evidence_bonus            # O 0.60 · R 0.35 · M 0.10
      + Σ signal_bonus            # closed_loop 0.50 · real_robot 0.40 · numbers 0.30
                                  # open_release 0.25 · latency 0.20
      + recency_bonus             # same day 1.0 · 1 day 0.8 · ≤3 days 0.5 · ≤7 days 0.25
      + (source_weight − 1.0)

Selection then applies min_score = 0.9, a per-lane cap of 2 and a global limit of 8, so a single hot topic cannot take over the page. Two soft caps sit next to it: filters.per_source (3) and filters.per_evidence (4). Selection makes two passes in rank order. The first pass honours every cap. The second pass fills whatever slots are still empty with the lane cap only. A soft cap therefore reserves room for other sources and evidence kinds but never leaves the page short. Without them, a day when the arXiv API refused and the category feeds answered came back as eight [R] preprints. The shortlist is always shown in rank order. Three further gates apply:

A fourth gate keeps one story to one slot. A vendor post and a trade outlet’s retelling have different URLs, so the repeat guard lets both through: on 2026-09-22 NVIDIA’s Isaac ROS 5.0 post and The Robot Report’s account of it took two of the eight places. Two titles tell the same story when they share at least three distinctive words and those make up at least half of the shorter title. Function words, press verbs and the names that appear in half the pool (NVIDIA, Google, robot, AI…) do not count, so two unrelated Isaac Sim posts or two Video Friday issues stay apart. The higher-ranked telling keeps the slot, and a story told on an earlier day inside the repeat window is not told again. Papers are exempt on both sides: two preprints with similar titles are two papers.

A day may publish fewer than eight picks, or none. When no live item meets these rules, the daily page says so and then lists the curated baseline under its own heading. The radar block in each README does the same: directly under the day’s heading and window line it quotes the same “no items” sentence, then a bold baseline label, then the table of curated entries. Before this, the README put the baseline under today’s heading with ranks and reasons, as if it were the day’s selection. Days with picks render unchanged.

4. Quantitative claim extraction

distill.extract_numbers matches percentages, multipliers, ms / Hz / fps, parameter counts, hours / episodes / demonstrations / trajectories / tasks, and GB / TB / W / kg / DoF. Up to four are shown per item, in source order, unmodified. Numbers are quoted, not recomputed — if the source is wrong, the radar is wrong in the same way, and the link is there to check.

A bare 48.1% · 68.9% does not say what was measured, so each figure on a page is followed by the few words around it in the source (distill.number_context): up to four before and three after, cut at the clause. The metric is not named by guessing from nearby words; in “raises success rate and tracking accuracy by at least 24% and 47%” the nearest metric to 24% is the wrong one, and a quote lets the reader see that. A figure is quoted where it stands alone, so 17.6% is not quoted from inside 12.1-17.6% when the text also gives it on its own.

Feed furniture is removed before anything is quoted: the WordPress footer “The post … appeared first on …” and the fixed Video Friday preamble.

Published figures can be reviewed against their sources (roadmap RA-01) with python3 -m pairadar.claims [--store PATH] [--days N] [--limit N]. The tool is offline and read-only. It reads radar/feed.json and prints a Markdown worksheet with one row per figure published in the --days (1–30, default 7) before the newest entry date. It never uses the wall clock. Each row gives the day, evidence tag, source link, a status, a quoted context and an empty review column. The header and a whole-window status line count every figure, even when --limit (default 30, no upper bound) caps the table. A truncation line then says how many unlocated and no-excerpt figures are hidden and gives the --limit value that lists them all. The statuses come from number_context over what the store kept:

No status verifies a date, unit, population or result. Items that are not objects, items without a _radar object, and entries whose date is not a canonical YYYY-MM-DD string are skipped and counted in the header. Non-string text fields are shown as empty. An unreadable store, invalid JSON, the wrong JSON Feed version, a non-list items or no dated entries exits 2 with one claims: line.

5. Translation policy

This is the part most repositories get wrong, so it is explicit here:

  1. Titles are never translated. They stay citable and searchable.
  2. The English abstract excerpt is an excerpt, rendered as a block quote, shown only for live (non-curated) items.
  3. The analytical layer is authored, not translated. Lane names, evidence labels, signal badges and the “why it matters” line come from data/glossary.json (ui, why_parts) and from hand-written trilingual fields in data/baseline.json.

    Without a curated or drafted line, the line is composed from what the item’s text shows (render.composed_why): what its evidence tag means, which of the checks expected for that tag it gives or leaves out (real-robot results, closed-loop results, figures for a preprint; figures for a release or a report), and what to watch in its lane. Hardware and safety items are not asked for a closed loop. A single sentence per lane used to be the whole line: the week of 2026-09-19 published 48 lines of which 8 were distinct, and a README opened with two identical ones.

  4. No machine translation is invoked anywhere in the pipeline. Titles, numbers and evidence tags are never produced or altered by a model.
  5. Drafted analysis is opt-in, per item, and labelled. The “why it matters” line may come from a draft in data/notes/<date>.json instead of the composed line. Such a file is written outside the pipeline by scripts/draft_daily_notes.sh, which runs on a maintainer’s machine on a schedule and never in CI. The pull request is merged automatically once every deterministic check passes and a reviewer agent approves it; no human reads it first, and the page says so. Precedence is fixed: a human-curated line beats a draft, a draft beats the composed line. Every drafted line carries a visible label next to its heading, the page names the agent and model that drafted it, and each language is drafted separately rather than translated. A missing language falls back to the composed line, and a malformed or foreign notes file is ignored with a log line: notes can never break a run or silently replace authored text.

6. Charts

Charts are emitted as hand-written SVG (pairadar/charts.py): lane distribution, daily cadence and evidence mix. No plotting dependency, no binary diffs, readable in a pull request. One set is written per language, plus an unsuffixed English set because already-published pages link to those filenames.

The lane and evidence-mix charts count the picks published in the last seven days (cli.CHART_DAYS), read from the feed store, and their titles say so. They used to add the 29 curated baseline entries to the day’s eight picks, so the bars mostly showed the baseline. Seven days, not thirty, because the store keeps at most 100 entries and eight picks a day would overflow a thirty-day count. The charts are embedded only in the READMEs, which always show the current day. A daily page is frozen when it is published, so it states its lane counts, source mix and recent cadence as text instead of linking a chart file that the next run overwrites.

Bundling a font was never necessary: these are SVG text, so the glyphs come from the reader’s browser and the bytes this repository commits are identical either way.

Each lane may declare a short chart_label per language in data/taxonomy.json. The full English names run 43–53 characters and, drawn at 12px from x=20, overran the bars that start at x=250. Without a font library the renderer estimates advance width per character — East Asian wide and fullwidth forms at one em, everything else at a deliberately generous 6.8px — and trims anything still over budget with an ellipsis.

The estimate is per character because a single average is wrong for CJK by a factor of two. Chinese lane names fit the gutter unshortened; Japanese ones did not, which was visible only after rendering the charts and looking at them: five of eight labels came back cut. Short Japanese forms fixed that, and a test now asserts that no lane label in any language needs trimming at all — a cut label is a content problem to fix in the taxonomy, not a rendering detail to tolerate.

7. The site

The Pages site opens on a landing page per language (index.html in Chinese, en/, ja/), written by pairadar/site.py from the same context as the day’s pages, so the two cannot disagree. It shows the day’s picks as cards (lane, evidence tag, the lead figure with its quote, the “why it matters” line) and the eight lanes as a radar: the filled shape is the last seven days of picks, and each of today’s picks is a dot on its lane’s spoke that links to its card.

The landing pages are plain HTML without front matter, so Jekyll copies them unchanged and no fetched text reaches Liquid; every fetched string is escaped. The daily, weekly and archive pages are still Markdown, rendered with _layouts/default.html, which takes each page’s language from its file name so the page says which language it is in.

A shared link is previewed from its Open Graph tags, and previewers read neither scripts nor SVG, so each language has a 1200×630 PNG card, assets/og.<lang>.png. It is drawn from the site stylesheet by python3 -m pairadar.site --og, which needs a local Chrome, and committed; the daily run never starts a browser. A share button links the day’s frozen page rather than the landing page, which changes every morning. CI builds the site with the same builder as Pages and checks each page’s language, stylesheet and card.

8. Reproducibility

9. Known limits