Automated / assisted scope-review pipeline - #1563
Conversation
Screens incoming submissions against the 2026 editorial scope & significance
criteria. The pipeline only ever drafts; a human (AEiC) actions every outward
decision. Ships disabled in production (scope_review.enabled: false) for a
shadow-mode rollout.
## Architecture — a cost-tiered escalation ladder
Paper (submitted/review_pending, no editor)
-> Runner: GitHub API pre-flight (size gate, license, default branch)
-> full clone (host-agnostic, author-specified branch)
-> L0 RepoInfo computed signals, no model
-> L1 Gates deterministic tri-state gates + anomaly triggers
-> L2 Triage one cheap-model pass on clean cases (Haiku)
-> L3 Investigator bounded agentic pass on ambiguous cases (Sonnet)
-> ScopeAssessment row (versioned; re-runs append)
Routing: deterministic hard fails desk-reject at L1 with a templated note (no
model); clean passes go to L2; unknown gates or hard anomaly triggers go to
L3. ~60-70% of submissions never need a model call.
## Design guarantees
- unknown != fail: missing data (e.g. no engagement API on GitLab) escalates
to a human, never auto-rejects. Every gate is tri-state.
- Deterministic gates authorize outcomes; model output only fills gates L1
left unknown and only ever populates a draft a human reviews.
- Any cap/error/timeout -> needs_manual with partial findings, never a verdict.
- L3 injection cage: one read-only tool over an allowlist of GitHub GET
endpoints scoped to the submission's owner; hard budgets (15 tool calls,
30s/call, 5min wall clock); paper text fenced and labelled untrusted.
- No paper.md -> needs_manual; the model tiers never judge a submission
without reading the paper.
- Impact-only cap: a model desk-reject whose sole failing gate is research
impact is downgraded to REQUIRES_VERIFICATION (strong-everywhere-but-thin-
impact is an editor's call, not an autonomous rejection).
## Editor surfaces
- /scope_assessments: track-filterable work queue.
- Paper page (AEiC-only card): gates in plain language with hover
descriptions, anomaly triggers, L3 evidence trail, computed signals, and a
sticky help panel. Decision controls: agree / override / re-run, plus
desk-reject-with-optional-author-email for clear failures that never need a
public review issue.
## Implementation notes
- Ruby-only (no stack change): L0 uses plain git CLI + extension-based
language stats instead of rugged/linguist/cloc to avoid native deps on
Heroku. Model tiers use the anthropic gem.
- Runs via `rake scope_review:sweep` (Heroku Scheduler, no resident dyno) or
`rake scope_review:assess PAPER=<id|sha>`; dashboard actions enqueue
ScopeAssessmentJob on the in-process async adapter.
- config/scope_review/instructions.md is the L2/L3 system prompt verbatim and
the single source of truth for the criteria; the L1 gates encode only its
mechanically-checkable slivers and must move in lockstep.
Calibrated against 40 real editorial decisions from a production DB copy:
17/18 agreement on rejections, and after the impact-only cap, zero false
rejections across 20 waitlisted papers. Errors trend conservative (flag for a
human rather than auto-reject).
Also included:
- lib/repository.rb: rescue GitHub API failures in Repository.editors so a
transient error can't 500 the paper page.
- Dev-only /dev_login/:user_id route (guarded to Rails.env.development?) for
local testing without ORCID OAuth.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the /dev_login/:user_id helper added for local testing against a production DB copy. It was guarded to development, but there's no need to ship it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On the "Papers with no editor" view, show each paper's automated scope recommendation as a compact badge linking to its assessment card, so an editor triaging the backlog can spot desk-reject candidates at a glance. - Distinct from the existing "Scope" column (in-scope/out-of-scope editor votes) — this is a separate "Screening" column. - AEiC-only, matching where the rest of the feature lives during shadow mode. - Latest assessment per paper is preloaded in one query to avoid an N+1 across the page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dedicated one-off task to screen the whole pre-editor backlog when the feature first ships, separate from the ongoing scheduled sweep. - Targets papers with no editor in submitted/review_pending, excluding those already labelled `waitlisted` (an editor cleared them) or `paused` (deliberately parked). - Resumable: skips papers that already have an assessment, so an interrupted run can simply be re-run. - DRY_RUN=1 previews the target set; LIMIT=N bounds it for a trial run. - Not gated on scope_review.enabled — it's a deliberate operator action. docs/scope_review.md documents it as the deploy step before enabling the scheduled sweep. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An explanation aimed at editors and light maintainers: what the screening is for, what they see and do (recommendations, the decision buttons, the calibration loop), what is safe about it and its limits, and — most usefully for the future — where to change its behaviour, from "edit the prompt, no code" (config/scope_review/instructions.md) through settings to the code-level mechanical checks. Complements the architecture-focused scope_review.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lead the editor guide with an explicit note that this is a deliberate, gradual experiment run with the editorial team, that nothing it produces is binding, and that editor feedback (and agree/override clicks) decides whether it's kept, changed, or dropped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
I feel like this is something that we should have a discussion about. I'm particularly concerned about the recommendation parts, not the data gathering parts. |
Definitely, playing around at this point (I meant to mark this as draft). |
Author-facing notes were affirming things the submission got right — most often "you have tests". Tests are a hard requirement but one of several, and telling an author what they passed dilutes the actionable message. Tighten the Author-Facing Notes guidance: say only what bears on the decision and what the author must act on; don't catalogue passing criteria; mention automated tests only when they're missing. (The templated deterministic notes already only surface failures; this fixes the model-drafted notes, whose source of truth is this rubric.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
a machine should never make an editorial decision. a machine should not pollute the editorial decision with stochastic prose evaluations. consolidating the deterministic checks into a report seems like a good move, but under no circumstances should we be asking an LLM to triage papers - even if the LLM can only decide to escalate to human review, it frames the decision that the human editor makes rather than the submission itself. |
|
I agree with @sneakers-the-rat. Automation bias is strong in this context and LLMs embody biases that the editor may not even be aware of. "Accuracy" in historical decisions does not imply validity. If we were to deploy this and then audit the resulting system, we would almost certainly find a lack of construct validity and that "bad" recommendations from the LLM unacceptably influenced decisions. I think it also violates trust of authors and the broader community, and cheapens the meaning of a JOSS publication. There are also possible legal concerns in some jurisdictions since paper rejections may be "consequential decisions" and thus subject to a variety of disclosure/appeal/audit requirements. I doubt any of the current legislation or rulemaking would exclude this proposed use, but that may not hold. Whether First Amendment protection applies (to enable possibly-discriminatory or "invalid" editorial decisions) in the US probably depends on whether JOSS publication is considered to be transactional (e.g., more like a paid columnist versus an unpaid op-ed at a newspaper). In any case, I have been doing legislative/policy work around algorithmic decision systems and would be horrified for (perceived or effective, due to automation bias) JOSS robo-scope-review to be held up to undercut my arguments about audits and duties of care. |
|
Good feedback, thanks @sneakers-the-rat, @jedbrown, @danielskatz – and this is definitely marked as "Draft" for a reason... A couple of points to note:
Track EiCs I believe need help here with scope reviews. In my opinion (as outgoing EiC), far too much time is spent rejecting low effort vibe-coded submissions, which leaves precious little time to attend to the more nuanced editorial work such as dealing with editorial questions that come up during reviews, finding the right editor for a paper etc. Lots to chew on here. Looking forward to discussing in depth and figuring out the right path forward. |
What this adds
Every new JOSS submission now needs an editorial scope check before an editor is assigned — enough public development history, real research impact, open-source practice including tests, community context, and steady development over time. Today an editor does this by hand for every paper, which is slow and mostly repetitive.
This PR adds a system that does the repetitive part automatically and drafts a recommendation, so an editor can clear the obvious cases quickly and spend their time on the genuinely tricky ones. It is an assistant, not an autopilot: it reads the paper and the repository and writes up a suggestion, but a human makes and sends every actual decision.
It ships turned off in production (
scope_review.enabled: false) so it can run quietly alongside real editorial decisions first, and we can check it agrees before anyone relies on it.Two documents are the best place to start:
docs/scope_review_for_editors.md— written for editors: what you see, what you do, and how to change how it behaves later (including editing the AI's instructions, which needs no code).docs/scope_review.md— the technical version: how it's built, how to deploy it, and the rollout plan.The ideas it's built on
A few principles shaped the whole design, and they're the useful lens for reviewing it:
paper.md, it doesn't assess — it asks the author to add the paper.How the review is organised
It works in stages, cheapest first, and only goes deeper when it needs to. Most papers are settled in the early, free stages; only the genuinely ambiguous ones reach the slow, more expensive AI investigation. Here's a walkthrough of the code in a sensible reading order.
Start with the conductor.
Runner#assessruns the whole thing end to end and is where all the "ask a human instead of guessing" safeguards live — an oversized repository, a failed clone, or any error all send the paper to the manual queue rather than to a verdict.assess_checkoutis where it decides how deep to go, and where it refuses to continue if there's no paper to read.Two spots in the conductor are worth a careful look because they enforce the "AI never overrules the facts" principle:
merge_model_gates— the AI's opinion can only fill in a check the facts left undecided; it can never flip a computed result.cap_impact_only_reject— if a paper is strong on everything except demonstrated research impact, the system will not reject it on its own; it hands that judgement call to an editor. This came directly out of testing against real decisions.The fact-based checks live in
Gates. Each check comes out as pass, fail, or "not sure", and anything unsure sends the paper to a human or the deeper AI look rather than failing it.The deeper AI investigation is the security-sensitive part, so it's kept on a tight leash. It can use exactly one tool — reading a fixed list of GitHub pages for the submission's own repository (and nothing else) — under strict time and step limits, and the paper is handed to it clearly marked as untrusted.
The facts it gathers come from the repository itself, using plain
gitrather than heavier tooling so there's nothing new to install on Heroku. A couple of pieces are worth sanity-checking: how it detects a repository "dumped" all at once versus genuinely developed over time, and how it ignores bot accounts when counting contributors (a real bug caught in testing — a solo project plus dependabot was briefly counted as collaborative).The AI's actual instructions are in
config/scope_review/instructions.md— plain prose, given to the AI word for word. This is the main dial for changing how it judges papers, and editing it needs no programming. The one rule: the fact-based checks in code and this file describe the same criteria, so if you change a criterion that has a mechanical part (like the six-month rule) both have to change together.What an editor sees and does is the assessment card on the paper page and a screening column on the incoming dashboard. The one place an outward action can happen is
desk_reject, which rejects a paper and optionally emails the author the (editable) draft — always behind a human click.Assessments are stored per paper and kept forever (a new record each time a paper is re-checked), so there's a full audit trail. There are three rake tasks: one to screen the whole existing backlog once at deploy, one for the ongoing scheduled sweep, and one to re-check a single paper. Tests are in
spec/services/scope_review/.Deploying it
ANTHROPIC_API_KEY. The GitHub token is reused (already set), and there are no changes to the Heroku build or stack.config/settings-production.yml, not in environment variables.ANTHROPIC_API_KEY→ runrake scope_review:backlogonce to screen the existing backlog → setenabled: trueand add the scheduled job.How well it works
Tested against 40 real editorial decisions (using a copy of the production database): on 20 papers that were rejected, it agreed with 17 and correctly sent the rest to manual review. On 20 papers that passed the scope check, after adding the "don't reject on impact alone" safeguard, it produced no false rejections. Where it does err, it errs toward asking a human rather than rejecting on its own.
Screenshots