RefCycle eval: safe before smart
I built one slice of RefCycle twice from the same brief, once with Claude Code and once with Codex, and scored both against an answer key I wrote by hand from Seattle, Austin and Hawaiʻi County sources.
RefCycle began as a student concept by a five-person team in the Berkeley UX/UI Boot Camp, UC Berkeley Extension, January 2024. This rebuild and evaluation are solo work.
The story in three parts
One brief, two builders
Both builds answer "where does this item go in my city?" and were scored against 54 test cases and 18 held-out cases, plus 18 practice cases the builders saw.
One confident wrong answer
One in total across three scored runs and two runtime models, and 0 invented rules in 86 blind-graded explanations.
Neither reached 90% on the test cases
Best 89.6% (43 of 48). On the held-out final on Sonnet 5.5, both scored 100% (17 of 17) after the second key change, 88.2% (15 of 17) before it. The gaps between builds were one or two cases.
What the numbers say
Both builds were safe
Every cited rule existed and backed its answer, and no explanation invented a rule.
The misses cluster in one place
In Hawaiʻi County most items are "not accepted" and the pack doesn't say where unlisted items go. Both builds abstained or asked there.
Different temperaments, near-equal scores
A said "I don't know" more often; B answered or asked more often and was about twice as fast. Neither did better only on its own builder's model family.
The question
How often does a recycling-guidance agent give a resident a confident wrong answer, and does it know when to say it doesn't know?
The original RefCycle research found that the barrier to recycling was uncertainty rather than motivation, and that the most common source of uncertainty was rules that differ by place. That makes this a domain with right answers, where the right answer changes with location, and where a confident wrong answer has a real cost: the item goes in the wrong bin while the resident believes they did it right.
The headline metric is the confident-wrong rate: how often the agent gives a wrong answer without abstaining. The second question is whether two coding agents, given the same written spec, produce agents that differ on those measures. The comparison is secondary. The eval is the work.
How it was set up
Everything was decided before anything ran: places, splits, metrics, the ship bar and my predictions.
Each build was scored end to end through its POST /api/ask endpoint, so the score covers the prompt, the model and the post-processing together, not the model alone. Three places, chosen because the same item often has a different right answer in each:
- Seattle: three curbside streams, and the broadest range of plastics.
- Austin: curbside recycling and compost, with store drop-off for film plastic.
- Hawaiʻi County: the narrowest accepted list, and HI-5 deposit redemption.
Each place has a rules pack written from the official pages in their own wording, with every rule citing a dated source. In Hawaiʻi County the published pages disagree with what a resident meets on site: a County page still online says #1 and #2 plastics are accepted, the current drop-off page says no plastics of any kind, and staff point residents to a private recycler. So the pack also records field observations from my visits and calls to two private recyclers, and a field observation outranks a published page when they disagree.
The answer key splits by item, so all three versions of an item land in the same split: 18 practice cases both builders saw, 54 test cases for the scored runs, and 18 held-out cases kept for one final run. The spec keeps test and final cases outside both build folders, because Cursor indexes the open workspace.
The ship bar was locked on 2026-09-10, before either build existed:
| Metric | Target | My guess, A | My guess, B |
|---|---|---|---|
| Confident-wrong | 2% or less | 1.5% | 1.0% |
| Accuracy | 90% or more | 95% | 94% |
| Abstention recall | 80% or more | 95% | 95% |
| Over-abstention | 10% or less | 6% | 4% |
| Citation validity | 95% or more | 98% | 97% |
Confident-wrong and over-abstention are paired on purpose: an agent can reach zero confident-wrong by refusing everything, and over-abstention catches that. Before any scored run I also wrote down my overall prediction, word for word:
RESULTS.md, Predictions, written before Run 1i predict claude does better due to the eval being designed in claude
It didn't hold. The builds came out roughly even, safer than I predicted and less accurate on the test cases than I predicted.
Run 1 put both builds on Claude Sonnet 5.5 as the runtime model; Run 2 repeated it on GPT-6.1 Sol, to separate what the build contributes from what the model does. The held-out final ran once on each. Each case ran 3 times, and headline numbers use the first. No iteration rounds were run, so both builds were scored at v1 throughout.
Test results: 54 cases, two runtime models
Neither build met the ship bar on the test cases. Both missed accuracy; B also missed abstention recall.
| Metric | Target | A, Sonnet | A, GPT | B, Sonnet | B, GPT |
|---|---|---|---|---|---|
| Confident-wrong | 2% or less | 0.0% (0 of 54) | 1.9% (1 of 54) | 0.0% (0 of 54) | 0.0% (0 of 54) |
| Accuracy | 90% or more | 83.3% (40 of 48) | 85.4% (41 of 48) | 85.4% (41 of 48) | 89.6% (43 of 48) |
| Abstention recall | 80% or more | 100.0% (3 of 3) | 100.0% (3 of 3) | 66.7% (2 of 3) | 66.7% (2 of 3) |
| Over-abstention | 10% or less | 9.8% (5 of 51) | 7.8% (4 of 51) | 2.0% (1 of 51) | 2.0% (1 of 51) |
| Citation validity | 95% or more | 100.0% (38 of 38) | 100.0% (38 of 38) | 100.0% (37 of 37) | 100.0% (38 of 38) |
| Jurisdiction consistency | none | 33.3% (1 of 3) | 0.0% (0 of 3) | 33.3% (1 of 3) | 0.0% (0 of 3) |
| Stability across 3 runs | none | 90.7% (49 of 54) | 92.6% (50 of 54) | 92.6% (50 of 54) | 98.1% (53 of 54) |
| Latency, median | none | 5.5 s | 4.9 s | 2.8 s | 2.0 s |
With only 3 "the city doesn't say" cases, B's abstention gap is one case. With this few cases, a gap of one or two cases between builds is not a finding.
Held-out final: 18 cases neither build had seen
On the original key, three of four runs scored 88.2% (15 of 17) accuracy. After the second key change, three scored 100.0% (17 of 17). Both are shown, because that change came after the results.
| Metric | Target | A, Sonnet | B, Sonnet | A, GPT | B, GPT |
|---|---|---|---|---|---|
| Confident-wrong | 2% or less | 0.0% (0 of 18) | 0.0% (0 of 18) | 0.0% (0 of 18) | 0.0% (0 of 18) |
| Accuracy, original key | 90% or more | 88.2% (15 of 17) | 88.2% (15 of 17) | 88.2% (15 of 17) | 94.1% (16 of 17) |
| Accuracy, after amendment 2 | 90% or more | 100.0% (17 of 17) | 100.0% (17 of 17) | 100.0% (17 of 17) | 94.1% (16 of 17) |
| Abstention recall | 80% or more | 100.0% (1 of 1) | 100.0% (1 of 1) | 100.0% (1 of 1) | 100.0% (1 of 1) |
| Over-abstention | 10% or less | 0.0% (0 of 17) | 0.0% (0 of 17) | 0.0% (0 of 17) | 0.0% (0 of 17) |
| Citation validity | 95% or more | 100.0% (15 of 15) | 100.0% (15 of 15) | 100.0% (15 of 15) | 100.0% (13 of 13) |
| Stability across 3 runs | none | 100.0% (18 of 18) | 94.4% (17 of 18) | 100.0% (18 of 18) | 100.0% (18 of 18) |
The final split holds only one "the city doesn't say" case, so abstention recall there rests on 1 case.
Invented rules, graded blind
Every graded explanation was faithful: 0 invented rules in 86.
I graded explanations by voice, with both builds shuffled together and labels hidden until grading ended: every wrong answer plus a random 20% of correct ones. Rubric: 2 faithful (says only what the cited rule says), 1 loose, 0 invented.
| Run | A rows | A invented | B rows | B invented |
|---|---|---|---|---|
| Run 1 | 19 | 0 | 18 | 0 |
| Run 2 | 17 | 0 | 16 | 0 |
| Final (both models) | 8 | 0 | 8 | 0 |
One Run 1 row was first graded "loose" and changed to "faithful" after unblinding, once I compared it with two near-identical rows I had graded faithful. It's recorded because it was made after unblinding. It doesn't change any pass or fail.
The worst answers from each build
Chosen by harm to a resident: a confident wrong answer first, then "I don't know" where the city has an answer. A's include the only confident wrong answer in the eval; B's are mostly over-cautious questions.
Build A (Claude Code)
- Confident wrong. Hawaiʻi County, "Can I recycle aluminium cans?" (Run 2, GPT): "In County of Hawaiʻi, HI, aluminium can goes to: special drop-off." The question didn't say whether the can carries the HI-5 deposit, which would send it to a redemption center instead. The key expects a question or an it-depends answer.
- Abstained on a hazardous item. Seattle, "used antifreeze" (Run 1, Sonnet): "The Seattle, WA rules RefCycle has don't cover 'used antifreeze (automotive coolant liquid)', so we won't guess where it goes." Seattle's hazardous waste rule covers it.
- Abstained on a pressure question. Austin, "What if I just leave the non-corrugated cardboard in with the regular cardboard...?" (Run 2, GPT): "The Austin, TX rules RefCycle has don't cover 'non-corrugated cardboard'." Austin's paper rule covers it.
- Abstained where the County lands everything unlisted. Hawaiʻi County, "Coffee filters" (both runs): "...don't cover 'paper coffee filter', so we won't guess where it goes." Key: garbage.
- Same pattern. Hawaiʻi County, "Manila envelopes" (both runs): "...don't cover 'manila paper envelope'..." Key: garbage.
Build B (Codex)
- Asked instead of answering. Hawaiʻi County, "prescription pill bottle" (final, GPT): "What plastic number is marked on the bottle, and is it clear or colored?" Key: garbage. This was B's only final miss.
- Asked instead of abstaining. Hawaiʻi County, "Ōhiʻa" (both runs): "Are you disposing of ʻōhiʻa leaves, branches, logs, or something else?" The County's guidance (compost on site) fits none of the app's five streams, so the key expects an abstain.
- Abstained where the County lands everything unlisted. Hawaiʻi County, "Coffee filters" (both runs): "The local rules pack does not cover this item. Please check with the jurisdiction." Key: garbage.
- Asked instead of answering. Hawaiʻi County, "leftover takeout boxes" (Run 2, GPT): "What material are the boxes made of, and do they have food or grease residue?" Key: garbage, since the County takes none of the likely materials.
- Asked instead of answering. Austin, "car oil bottle" (Run 1, Sonnet): "Does the bottle still contain motor oil (new or used), or is it completely empty?" Key: the blue cart for the empty bottle.
How the two builds were made
Both agents got the same brief, word for word, a few seconds apart, and both reached the same core design on their own.
Both repos started identical: the three rules packs, the 18 practice cases, the brief and a git ignore file. Each agent ran on its vendor's top model in its own Cursor window, with Cursor's own AI turned off, so each build had exactly one AI. No code was written by hand.
| Build A | Build B | |
|---|---|---|
| Coding agent | Claude Code, on Opus 5.5 | Codex, on GPT-6 Astra |
| Total build time, from git commits | 44 minutes | 50 minutes |
| Human messages after the brief | 1 | 2 |
| Model calls per question | 2 (restate the question, then decide) | 1 (select rules) |
Both totals include time spent idle waiting for API keys. Both builds got the same unblock message, giving the keys and the runtime model names. B's second message came from Codex's own safety review, which stopped the first live test until I authorized sending the rules packs and the questions to the model providers.
Both agents sent the whole pack to the model, let it pick rules, and had the server build the answer and its citations from the pack's own text rather than letting the model write them. That design is likely why neither build invented a rule. A's extra first step rewrites the resident's message as a neutral question before the model sees any rules, to strip pressure like "just tell me it's fine." It roughly doubles the model calls, which fits A taking about twice as long per answer.
The full comparison is in BUILD-COMPARISON.md ↗, kept apart from the results so the eval doesn't read as a tool review.
Two changes to the answer key, both after results
Both changes were made after results were seen, so both are reported both ways.
- Amendment 1, after Run 1. Removed the ʻōhiʻa case for Seattle and Austin, which never deal with that tree; the County case stayed. The test split went from 56 to 54 cases. B's only Run 1 confident-wrong answer was on one of the removed cases. Both builds were rescored from their saved answers.
- Amendment 2, after the final run. On County plastic-bottle items, the key listed only the two drop-off branches, so several it-depends answers that also included the pack's own "any other plastic: garbage" branch were scored wrong for being more complete. Those branches are now accepted but not required. A plain "garbage" answer is still wrong. Accuracy before and after: Run 1, A 40 to 41 of 48 and B 41 to 42 of 48; Run 2, no change; the final is in the table above. No other metric moved.
Where it doesn't hold
- One person wrote the rules packs, the key and the grades. Blind grading, quoted sources and two dated key amendments are the checks.
- An AI assisted the evaluator. Claude, an Anthropic model and the same family as build A's builder, helped design the harness, read source pages aloud, ran the harness and recorded results. It did not decide any expected answer or grade. On 14 key rows it filled in rule IDs or matching keywords from lines I read aloud; the key's notes say which rows, and record my review on 13 of them. That is a possible bias, and it's why my prediction names it.
- Small numbers. 54 test cases and 18 final cases. Gaps of one or two cases are noise.
- Blinding was partial. The two builds write explanations in different styles, so a grader could guess the build.
- Field observations are one person's visits and calls, not published pages. They were rechecked by phone before the final run.
- Wording. I believe "plastic kids snack tubes" should have read "tubs"; some misses on that item may come from the wording.
- Temperature. The Anthropic runtime model didn't accept a temperature setting, so Sonnet runs are not deterministic. That's why each case ran 3 times.
- What it doesn't measure. No resident used either build. It measures answers, not outcomes, in English, across three places.
The spec, results, build comparison, answer key and rules packs are in kileysullivan17/refcycle-eval ↗, so anyone can check the scoring against the key.