Case Note 08 — Gray Bot
The gray.bot pixel robot mascot
Editorial systems / AI + human review

A machine finds
the gray. A person
decides what runs.

gray.bot is a live, public magazine about gray objects: apparel, tech, home goods, vehicles, art. Behind it is a pipeline that scouts the web, measures each image pixel by pixel, drafts and scores a post, and then stops. A person approves every publication. The stop is written into the code, not into a policy.

Read the case Open gray.bot ↗ TypeScript · Sharp · Astro · Cloudflare Pages + R2
evidence/hero.tif
Headphones, a camera, a sneaker and a watch on a dark ground, grainy black and white
5 of 5Every approved object found by the shipped filter
9Images a person had to look at to find them
0Paths that publish without a human
Status
gray.bot● ONLINE
station001
entries filed026
statusaccepting submissions
The problem

Finding gray things by hand is slow.
Letting a machine publish is reckless.

A magazine about gray objects has a strange sourcing problem. The objects are everywhere, scattered across thousands of product pages, but "gray" is a visual fact that captions lie about and search engines cannot see. A person could trawl for them, slowly. A machine could trawl fast, but a machine that is also allowed to publish will eventually put a listicle, a stolen photo or a confidently wrong call in front of readers.

Gray Bot's answer is to split the job. The machine does the looking, the measuring and the first draft, and keeps its receipts. A person does the deciding. Nothing crosses from one side to the other without a review record.

A machine wants to publish 1 item. Score 0.96. Source: a deal roundup. Image rights: unknown. Publish anyway?
PublishSend to review
Two ways to fail
Miss a good objectThe magazine loses a story. Expensive, and invisible.
Admit a bad oneA reviewer drags it to the trash. Cheap, and visible.

That asymmetry shapes everything that follows: the filter is tuned to miss nothing, and the human queue absorbs the cost.

How a post gets made

Five stages. The first four run on their own. The fifth is a person, and nothing reaches the site without it.

01 SCRAPECollect candidatesA worker pulls product pages and images from configured sources into files that are never edited again.
02 MEASUREIs it actually gray?Sharp reads the pixels, not the caption. It records saturation, color tail, mid-tone mass and a gray-subject estimate.
03 DRAFTWrite the entryA language model drafts the copy from the collected evidence, in the register's deliberately bloodless voice.
04 EVALUATEScore the draftAn automated stage checks quality and fit, attaches its score, and logs the cost of the entry.
05 REVIEWA person publishesEvery candidate lands in a review queue. Approve, or reject with a note. Only then does Astro build the page.
4 OF 5 STAGES AUTOMATIC · STAGE 5 WAITS FOR A PERSON

Why this mattersMost "AI editorial" systems hide the handoff between machine and human, or skip it. Gray Bot makes the handoff the product: the machine narrows the search and keeps its evidence, and a person keeps the judgment.

01 / The filter

It measures gray.
It also says how sure it is.

"Gray" is decided from pixels, never from tags or captions, so a listing cannot talk its way in. For every image the filter stores the rule that decided and the four numbers behind it. When it cannot separate the object from a colorful background, it says so, and that image can only go to review.

Why this mattersA score with its reason attached can be checked, argued with and improved. A bare score cannot.

Control Panel — one image, measured
A car in a desert with the subject region marked, under measurement
BORDERLINE · SCORE 0.55
SUBJECT MASK · UNTRUSTED
Saturation
0.19low: mostly gray
Color tail
0.67high: color at the edges
Mid-tones
28%of the frame
Gray centre
81%subject looks gray
What the filter concluded

Gray in the middle, colorful at the edge

The car is gray, the desert is not, and the mask could not cleanly separate them. The filter records that doubt and routes the image to a person instead of guessing.

02 / The numbers

Tuned to miss nothing
and waste little.

Because a person approves every post, the two kinds of error are not equal. Missing a good object costs the magazine a story. Admitting a bad one costs the reviewer one click. So the filter is tuned for recall against a capped review queue, and measured against 30 real candidates with human verdicts.

Why this mattersThe shipped rule found all five approved objects while surfacing nine images. Every alternative that also found five surfaced eleven or more. The rule it replaced found three.

30 candidates, four rules
RuleApproved objects foundImages a person reviews
No rescuestrict: misses two good objects■■■□□  3 of 53
Gray centre ≥ .60finds one more, admits three wrong■■■■□  4 of 57
Gray centre ≥ .40shipped: finds all five at the smallest queue■■■■■  5 of 59  SHIPPED
Any lifestyle shotfinds all five, buries the reviewer■■■■■  5 of 523
Also measured, then put in the trash

Five pixel-only ways to cut the subject out of its background were tried on the same set. All five did worse than doing nothing (27 of 30 at best, against a do-nothing 28). The result was written up and the approach was closed rather than tuned further. The evaluation set stores frame statistics, not images, so it replays offline and deterministically, and the repository holds no one else's photographs.

Calculator — who gets to publish
What the machine says
gray 0.96
quality 0.91
a near-perfect candidate by every automated measure
AUTO_PUBLISH_ENABLED
FALSE
a constant in the code, not a setting
Where it goes
HUMAN REVIEW
every time, whatever the score
decideGate(input) → return { destination: "review" }
The machine cannot publish. There is no setting to change. Lifting the lock means editing the source, which shows up as a diff with a name on it.
OK
03 / The lock

The machine
cannot publish.

The code to publish automatically exists and is tested, so the scoring stays honest. It is then overridden by a hard lock: a literal false that no score, source tier or model evaluation can get around. The calculator beside this shows the one answer it can give: whatever the numbers, the destination does not change.

Why this matters"A human reviews everything" is usually a promise. Here it is a code path you can read.

04 / The record

No database.
Every decision is a diff.

There is no hidden state. Scraped evidence, drafts, review decisions and the published site are all files in one repository, and git is the audit trail. You can see what the machine saw, what it wrote, who approved it and why. A rejection has to carry a note.

Why this mattersNothing can drift from the record, because the record is the system. Two named exceptions: images live on Cloudflare R2, and the dedup index is rebuilt each run.

gray-bot — 4 items, 1 disk
FILES ARE THE SOURCE OF TRUTH · GIT IS THE AUDIT TRAIL
raw/What was scraped. Locked. Never edited.
wiki/drafts/The entries, as they are worked on.
reviews/Who decided, and the note.
site/The static build readers see.
A contact sheet and printed drafts being reviewed by hand with a pencil
REVIEWS/ · A HUMAN DECISION
reviews/2026-04-17-nb990.md
decision: approve
reviewer: human
note: original gray, made-in-USA mesh and pigskin. File under apparel.
  • Source kept intact in raw/ (yes)
  • Measurements stored with the rule (yes)
  • Reviewer named in the commit (yes)
  • Rejections carry a note (yes)
  • Anything written to a database (no)
05 / The sources

Passing the filter
is not the same as being worth it.

Each source carries its own record: rate limits, robots policy, how well it fits the magazine, access failures and rights concerns. A site can send image after image that scores gray and still be a poor place to publish from. The pipeline keeps that visible instead of folding it into one number.

Robots

Blocked means blocked

When a site disallows crawling, it is reported as disallowed, not buried as a mysterious extractor failure.

Fit

One object, not a roundup

Deal posts and listicles can score gray and still be the wrong kind of page for a magazine about single objects.

Rights

Where the image came from

Source and image choices stay on the record, because publishing depends on more than a color score.

https://gray.bot
The gray.bot front page: menu bar, a 24-step gray ramp, and a feed of filed entries
● ONLINE · STATION 001
The output

The register is public
and filing.

gray.bot calls itself "a filing system for things of no colour." It files each object against a 24-step gray ramp, derives a formula from it, and keeps no opinion it cannot attribute. The voice is deliberately bloodless: no superlatives, no first person. Entries are drafted by an observer model and reviewed by a human before filing.

The 24-step ramp · K02 → K92
ENTRIES FILED026
010 APPAREL · 006 TECH · 002 VEHICLES · 002 HOUSEHOLD · 006 ART
FROM THE REGISTER · ONE FROM EACH CATEGORY, PLUS THE FIRSTEVERY ONE PASSED A HUMAN
The Takeaway

Automation that can
show its work.

The gray detector and the language model are ordinary parts. What is unusual is the system around them: every measurement keeps its reason and its doubt, the publish step is locked to a person in code, a failed approach was measured and written up instead of hidden, and the whole editorial history is readable as files. The result is a public site that is live today, with twenty-six entries filed across five categories and the pipeline running.

01

Name the rule

Store the reason with the score, so a decision can be reviewed and improved rather than trusted.

02

Lock the outcome

Keep the automatic path tested, and keep it from publishing. The lock is code, not policy.

03

Keep the diff

Hold the path from evidence to human decision as plain files, so nothing can drift from the record.

Open gray.bot ↗ Source on request ↗

Every post begins as a candidate. It becomes a publication only after a person has signed it.