about

janitor-bot watches every MLB game for one thing: an outfielder who guns down a baserunner. It grades the throw, posts the clip, and lets the channel argue. A weekly agent then checks its own grading against how people actually reacted. Five stages, top to bottom.

detect

stage 1 of 5

watch the play-by-play

It polls the MLB Stats API for every game in progress. When a runner is retired and an outfielder started the throw, that play gets pulled out of the feed — nothing else counts as an outfield assist.

The raw play description is what triggers everything downstream.

real play · from plays table
“Hao-Yu Lee singles on a sharp line drive to left fielder Cody Bellinger. Riley Greene out at home on the throw, left fielder Cody Bellinger to catcher Austin Wells.”
parsed to → LF → C, target base Home, Cody Bellinger throws out Riley Greene.
tier

stage 2 of 5

score the throw

Each play earns points and lands in one of three tiers. Target base pays the most (Home 4, 3B 3, 2B 1). A direct throw adds 2; a relay with 3+ fielders subtracts 2. Available video adds 1, and an out that only stood because of a replay overturn subtracts 2.

A throw clocked at 95+ mph adds 1 more, on the rare play where Statcast velocity is on hand.

Totals map to high (5+), medium (3–4), and low (0–2).

real credit chain · scores medium
RF → SS → C   target base Home   video yes
target base: Home+4 relay chain (3 segments)−2 video available+1 score 3 → medium= 3
A throw home would tier high on its own, but the cut through the shortstop is a relay — the −2 penalty drops it to medium.
post

stage 3 of 5

drop it in slack

The play goes to the channel with its Baseball Savant video. When Savant carries more than one camera, every angle is posted — home, away, and high-home — not just the first one it finds.

janitor-bot APP · 7:14 PM
🔥 CF → C · Home · high
Ceddanne Rafaela guns down Edouard Julien at the plate (MIN @ BOS, top 7)
home   ▶ away   ▶ high-home
react 🔥 if it rips · 🗑 if it’s a nothing play
vote

stage 4 of 5

let the channel judge

Teammates react 🔥 for a real gem and 🗑 for a dud. Votes are snapshotted per play into a fire count, trash count, and net score.

When the crowd and the tier disagree — a low play people love, a high play they ignore — the play is flagged for the weekly review to look at.

real tally · from vote_snapshots
LF → C · Elly De La Cruz throws out Matt McLain at home
🔥 fire 3 🗑 trash 0 net +3
Not every play lands this cleanly. A separate throw home this season pulled 0🔥 / 2🗑 (net −2) against a medium tier — flagged channel_disagrees_high_or_medium for review.
review

stage 5 of 5

audit the week

Once a week an agent reads every detection against its votes and the prior weeks’ history. It files findings about where the scoring and the channel disagree, each tagged to a suspected rule area.

A human marks each finding confirmed or rejected. The confirmed ones become the next change to the tiering rules — the relay penalty above came out of exactly this loop.

real finding · from agent_findings
severity: info trend: recurring outcome: confirmed
“Play 333 (LF→SS→C→3B, medium tier, CWS@PHI) is the only play this week to receive a trash vote (net score −1). The credit chain involves four hops — the longest relay chain in the week’s dataset — yet the play was classified medium rather than low.”
rule area: new_tunable_needed · directionally consistent with a prior confirmed finding on relay length → fed the relay penalty now in ranking.ts.
api

The same data behind these pages is served as JSON. All endpoints are GET and take no auth.