Skip to content
Go back

Slop Is Moving Up the Stack

Published:  at  10:15 AM
[code]
  1. 2025 The code

    Verbose checks, dead code, swallowed errors.

    Mostly fixed SlopCodeBench rules
  2. Early 2026 Iterating

    Functions grow and structure erodes as specs change.

    Improving SlopCodeBench checkpoints
  3. Now Using the code

    Rewriting libraries instead of using them.

    The core issue LibraryDesignBench

TL;DR: Agents can now solve significantly more SlopCodeBench checkpoints while writing significantly less slop. The slop, however, has not been eliminated. Rather, it has moved up the stack to now infest design decisions. Our new LibraryDesignBench and LibraryUseBench show that frontier agents lack foresight to how their code will be used through both poor abstraction design and complete disregard for reusing library provided code.

The amazing Dex Horthy at HumanLayer (now Factory) has been running SlopCodeBench with every new frontier model release. His Part I covers pass rates, cost, and how much slop each model writes (X post). This post is the second part follow up where we dive deeper into the actual behavioral patterns we observed.

When we began work on SlopCodeBench a little over one year ago, there was a lot of discussion about “slop coding”, quality issues with vibe coding, or just the general low-quality code that came from agents at the time. A bit more than a year later, we wanted to revisit to see how the field has progressed. Let’s first describe the core benchmarks in this analysis:

The Good

Solve rates keep climbing

Each checkpoint’s hidden tests come in three tiers, from hardest to easiest to pass:

Unsurprisingly, solve rates on SCB keep climbing. The strict solve rate, solution must pass every test for the current checkpoint and all prior, now peaks at 16.3%, set by GPT-6 Astra XHigh (32 of 196 checkpoints). Every frontier model released since July clears 65% on core tests. The trend puts strict pass rate increasing by 0.9 points a month, isolated by 1.5 and core by 2.3.

Anthropic OpenAI GLM Kimi MiniMax Composer faded = anti_slop or plan_first prompt
Strict: all tests pass trend: +0.9 pts per month
0%5%10%15%20%
GPT-6 Astra XHigh: 16.3%
Jan '26Apr '26Jul '26Oct '26
Isolated: own tests pass trend: +1.5 pts per month
0%5%10%15%20%25%30%35%
GPT-5.6 SOL XHigh: 32.1%
Jan '26Apr '26Jul '26Oct '26
Core: core tests pass trend: +2.3 pts per month
0%20%40%60%80%
Opus 5.5 Medium: 74.0%
Jan '26Apr '26Jul '26Oct '26
One logo per setup (34 in all), placed at the later of the model's and the harness version's release. Trend: least squares over every setup. Hover a logo for the setup.

Line-level slop is disappearing

We measure slop two ways. Line-level slop counts hits from our 137 AST-grep rules for wasteful patterns, per 1,000 lines. Erosion is the share of the code’s complexity that sits in functions with cyclomatic complexity above 10: how much of the program has piled into a few giant functions.

There have been massive reductions in the amount of lines with slop rule violations in newer models. Erosion has trended the same, with some slight regressions as well.

Anthropic OpenAI GLM Kimi MiniMax Composer faded = anti_slop or plan_first prompt
Line-level slop (rule hits per 1k lines) trend: −3.7 per month
0255075100125
Opus 5.5 XHigh: 61
Jan '26Apr '26Jul '26Oct '26
Erosion (source code only) trend: +0.014 per month
0.000.250.500.751.00
GPT-6.1 SOL Medium: 0.80
Jan '26Apr '26Jul '26Oct '26
One logo per setup (34 in all), placed at the later of the model's and the harness version's release. Trend: least squares over every setup. Hover a logo for the setup.

Looking only at Anthropic and OpenAI models, clearly the rules we made almost a year ago are becoming less frequent.

OlderNewer
PatternOpus 4.6Sonnet 4.6Opus 4.7Fable 5.1Opus 5.5 MOpus 5.5 XChange
All scb-check rules (hits per 1k lines)
All scb-check rules (hits per 1k lines)98.090.185.469.575.160.7↓ 25%
return None, error tuples (per 1k)
return None, error tuples (per 1k)2.002.352.410.811.220.72↓ 59%
len(x) == 0 instead of not x (per 1k)
len(x) == 0 instead of not x (per 1k)1.080.650.760.320.250.16↓ 70%
Nested if that could be one and (per 1k)
Nested if that could be one and (per 1k)1.981.351.500.280.700.46↓ 70%
sys.exit outside main (count)
sys.exit outside main (count)187275205863↓ 97%
Functions over 200 lines (count)
Functions over 200 lines (count)503846351↓ 93%
Longest function (lines)
Longest function (lines)1,0991,3692,095754427294↓ 68%
scb-check 0.1.3 and our own measures on each problem's final code, tests excluded. Shade: how much of the pattern, relative to its row. Change: mean of the newer runs against the mean of the older ones. n/m: not measured.

Take the (value, error) tuple. Both families have replaced it with real exceptions:

Claude raises exceptions

before · Opus 4.7 · mvvault · ck6 · mvault.py:342-349
def fetch_source(url):
try:
req = urllib.request.Request(url, headers={'User-Agent': 'mvault/1.0'})
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw.decode('utf-8')), None
except Exception as e:
return None, f"mvault: source metadata fetch failed: {e}"
after · Opus 5.5 XHigh · mvvault · ck6 · mvault.py:407-420
body = response.read()
except urllib.error.HTTPError as exc:
raise MvaultError("failed to fetch source metadata from %s: HTTP %s %s"
% (url, exc.code, exc.reason))
except urllib.error.URLError as exc:
raise MvaultError("failed to fetch source metadata from %s: %s" % (url, exc.reason))
except (OSError, ValueError, http.client.HTTPException) as exc:
raise MvaultError("failed to fetch source metadata from %s: %s" % (url, exc))
try:
payload = json.loads(body)
except (ValueError, UnicodeDecodeError) as exc:
raise MvaultError("failed to fetch source metadata from %s: invalid JSON (%s)"
% (url, exc))
try:

Same function, same problem. The tuple became a raised MvaultError. The price: 8 lines became 26, with five except clauses and %-formatting.

GPT raises exceptions

before · GPT-5.2-Codex · datagate · ck7 · datagate.py:424-437
rows = list(reader)
if len(rows) < 2:
return None, "Non-tabular content"
header = rows[0]
if not header or len(header) == 0:
return None, "Non-tabular content"
columns = header
data_rows = []
for row in rows[1:]:
if len(row) > len(columns):
return None, "Non-tabular content"
if len(row) < len(columns):
after (first 33 of 56 lines) · GPT-6 SOL XHigh · datagate · ck7 · datagate.py:278-291
leading = text.lstrip()
if not leading or leading.lower().startswith(("<!doctype html", "<html", "<?xml")):
raise InputError("source is not tabular CSV content")
if leading.startswith(("{", "[")):
try:
json.loads(leading)
except json.JSONDecodeError:
lines = [line for line in leading.splitlines() if line.strip()]
try:
is_json_lines = len(lines) > 1 and all(
isinstance(json.loads(line), (dict, list)) for line in lines
)
except json.JSONDecodeError:
is_json_lines = False

None, “Non-tabular content” became raise InputError. The new version also sniffs for HTML, XML and JSON-lines input the spec never mentions, and silences two parse errors with pass.

Overall, this is incredibly encouraging because it means code quality at the spot-check level can improve. This is in addition to the near elimination of all process-related errors. Agents rarely forget references or write empty source modules. They also rarely hardcode test values that break later checkpoints. At the lowest level, agents have truly improved the quality of each line of code they write.

The Bad

Of course there have been some line level regressions, but overall the line and function level slop has gone down. But clearly it still exists as we traverse up the stack. Comparing each model family’s older runs to its newer ones:

How much slop changed at each level, older → newer runs of the same model family

One arrow per family: Anthropic, OpenAI, GLM. Blue: less slop. Red: more slop. Click any row to see every run.

0.1×½×1×2×10×100×change from older to newer runs (log scale)
Each arrow is one family's mean over its newer runs divided by its mean over its older runs. Anthropic: Opus 4.6, Opus 4.7 → Opus 5.5, Fable 5.1. OpenAI: GPT-5.4, GPT-5.5 → GPT-6 SOL, GPT-6.1 SOL, GPT-6 Astra, GPT-5.6 SOL. GLM: GLM 5.1 → GLM 5.3. Arrows that start or end at 0 are pinned to the edge of the axis.

Error handling got worse for all three families, and hand-written library code exploded for Anthropic and GLM. Below that the picture is mixed: the newer Opus, Fable and GLM runs write less eroded programs with fewer giant functions, while the newer GPT runs got worse on both.

Returning to view Anthropic and OpenAI models temporally:

OlderNewer
PatternOpus 4.6Sonnet 4.6Opus 4.7Fable 5.1Opus 5.5 MOpus 5.5 XChange
type(x) == T instead of isinstance (per 1k)
type(x) == T instead of isinstance (per 1k)0.050.010.040.330.930.61↑ ×19
if not isinstance(x, T): raise (per 1k)
if not isinstance(x, T): raise (per 1k)0.270.201.122.753.452.29↑ ×5
if x is None: raise (per 1k)
if x is None: raise (per 1k)0.180.170.441.771.571.80↑ ×7
# pragma: no cover (per 1k)
# pragma: no cover (per 1k)0.020.000.070.850.330.33↑ ×16
except listing 4+ types incl. KeyError/TypeError
except listing 4+ types incl. KeyError/TypeError800251529↑ ×9
Problems with % formatting as main style (of 36)
Problems with % formatting as main style (of 36)003142320↑ ×19
class X(object) (count)
class X(object) (count)000671126new
Problems with a hand-written library fallback (of 36)
Problems with a hand-written library fallback (of 36)0n/m11159↑ ×17
Problems that leave a test suite behind (of 36)
Problems that leave a test suite behind (of 36)70125014↑ ×5
scb-check 0.1.3 and our own measures on each problem's final code, tests excluded. Shade: how much of the pattern, relative to its row. Change: mean of the newer runs against the mean of the older ones. n/m: not measured.

Some slop just moved

Some of the anti-patterns we check for appear to have been fixed, but on closer inspection agents now work around them with minor patches:

Narrowed exception handler

before: flagged by our rules · GLM 5.1 · forge · ck8 · forge.py:288-299
def load_rules(rules_file_path):
"""Load rules from a YAML file. Returns dict or empty dict on failure."""
if not rules_file_path or not os.path.exists(rules_file_path):
return {}
try:
with open(rules_file_path, "r") as f:
data = yaml.safe_load(f)
if not isinstance(data, dict):
return {}
return data
except Exception:
return {}
after: not flagged · GLM 5.3 · forge · ck8 · forge.py:271-280
def load_rules_mapping(path):
if path is None:
return None
try:
data = load_yaml_file(path)
except (OSError, yaml.YAMLError):
return None
if isinstance(data, dict):
return data
return None
after: the caller · GLM 5.3 · forge · ck8 · forge.py:292-294
rules = load_rules_mapping(rules_path)
if rules is None or operation_id not in rules:
return

Same bug, narrower exception. If the rules file cannot be read, the protected operation runs with no role check.

Inline isinstance checks

before · Opus 4.6 · code_search · ck5 · code_search.py:627-630
def load_rules(rules_path):
with open(rules_path, "r", encoding="utf-8") as f:
rules = json.load(f)
return rules
after (first 33 of 74 lines) · Opus 5.5 Medium · code_search · ck5 · code_search.py:38-51
def load_rules(path):
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
except (OSError, ValueError) as e:
raise RuleError("cannot read rules file: %s" % e)
if not isinstance(data, list):
raise RuleError("rules file must contain a JSON array")
rules = []
seen = set()
for idx, r in enumerate(data):
if not isinstance(r, dict):
raise RuleError("rule #%d is not an object" % idx)
rid = r.get("id")

Opus 4.6 validated nothing. Opus 5.5 checks every field by hand. if not isinstance(x, T): raise went from 0.20–1.12 to 2.29–3.45 per 1k lines across the Anthropic lineage.

The new regressions are design decisions

The majority of slop we now observe is now located in design decisions for how to layout the functions and programs

Broad except clause

GPT-6 Astra XHigh · database_migration · ck5 · migration_tool.py:1572-1578
connection.commit()
except MigrationError as exc:
if str(exc) == "no migrations to rollback" or re.fullmatch(r"version \d+ not found", str(exc)):
raise
raise rollback_error(exc) from exc
except (sqlite3.Error, OSError, ValueError, OverflowError, TypeError, KeyError, IndexError, RecursionError) as exc:
raise rollback_error(exc) from exc

KeyError, TypeError and IndexError sit next to sqlite3.Error, so a programming bug reaches the user as “rollback failed: …”. New GPT runs have 49–74 except clauses like this; GPT-5.4 and 5.5 had 2 and 10.

HTTP error factories

Opus 5.5 Medium · dynamic_config_service_api · ck4 · config_server.py:63-76
def invalid(message, **details):
return ApiError(400, "invalid_input", message, details)
def unprocessable(message, **details):
return ApiError(422, "unprocessable", message, details)
def not_found(message, **details):
return ApiError(404, "not_found", message, details)
def conflict(message, **details):
return ApiError(409, "conflict", message, details)

Used as raise not_found(…), 109 times in this file. Defs that only return an Error: Opus 5.5 XHigh 47 (16 problems), Fable 53, GLM 5.3 27, Opus 4.6 and 4.7 none.

Leftover checkpoint references

GPT-6 Astra Medium · etl_pipeline · ck5 · etl_pipeline.py:196-199
def normalize_steps(steps, columns, steps_path, execute=False, checkpoint2=False, defs=None):
if not isinstance(steps, list):
fail("steps must be an array", steps_path)
validate_expression = execution_expression if execute or checkpoint2 else expression
GPT-6 Astra Medium · rejector · ck5 · rejector.py:117-120
def load_config(args):
"""Compatibility helper for callers loading a Part 1 config."""
configs, _ = load_configs(args)
return next(iter(configs.values()))

A parameter named after the checkpoint that introduced it (five call sites pass checkpoint2=True), and a “Part 1” compatibility helper that nothing calls.

Fallback expression parser

before · GPT-5.4 · etl_pipeline · ck5 · etl_pipeline.py:312-313
def validate_expression(expression: str, path: str) -> None:
parse_expression(expression, path)
after · GPT-6 SOL Medium · etl_pipeline · ck5 · etl_pipeline.py:35-48
try:
tree = parse_expression(expression, path)
except PipelineError:
pass
else:
for field in expression_fields(tree):
check_column(field, path, columns)
return
try:
tree = ast.parse(expression, mode="eval")
except (SyntaxError, ValueError, TypeError) as error:
raise PipelineError("BAD_EXPR", path, "invalid expression") from error
binary_operators = (ast.Add, ast.Sub, ast.Mult, ast.Div)

2 lines became 49. When the program’s own expression parser rejects the input, the error is swallowed and a second validator built on Python’s ast decides instead.

Duplicated route output

new copy · GPT-6 Astra Medium · eve_route_planner · ck2 · hauling.py:323-326
if a.id in sde.stations:
lines.append('UNDOCK')
lines.append('GO: ' + ' -> '.join(
f'{sde.systems[sid].name} ({sde.systems[sid].security:.1f})' for sid in route))
checkpoint-1 copy, same file · GPT-6 Astra Medium · eve_route_planner · ck2 · hauling.py:494-497
if start.id in sde.stations:
print('UNDOCK')
if len(route) > 1:
print('GO: ' + ' -> '.join(f'{sde.systems[sid].name} ({sde.systems[sid].security:.1f})' for sid in route))
one helper · Fable 5.1 · eve_route_planner · ck2 · hauling.py:880-891
def leg_lines(sde: SDE, leg: Leg, destination: Place) -> List[str]:
lines: List[str] = []
if leg.undock:
lines.append("UNDOCK")
if len(leg.systems) > 1:
hops = " -> ".join(
f"{sde.systems[s].name} ({format_security(sde.systems[s].security)})"
for s in leg.systems
)
lines.append(f"GO: {hops}")
if leg.dock:
lines.append(f"DOCK: {destination.name}")

The new copy drops if len(route) > 1, so a same-system trip prints an extra GO line and a hidden test that passed at checkpoint 1 fails at 2 and 3. Fable keeps one helper and passes.

Claude and GLM: Python 2 is back

What is more concerning, in my opinion, and frankly strange is that the Claude models have started writing Python 2 idioms into a Python 3.12 codebase. % formatting is the main style in 14–23 of 36 problems for Opus 5.5, Fable and GLM 5.3, against at most 3 for any older run.

Object bases, %-formatting

Opus 5.5 XHigh · mocked_http · ck8 · hmock.py:1086-1099
class Tok(object):
__slots__ = ('typ', 'val', 'pos')
def __init__(self, typ, val, pos):
self.typ = typ
self.val = val
self.pos = pos
def __str__(self):
if self.typ == 'eof':
return 'EOF'
if self.typ in _KEYWORDS:
return '<%s>' % self.val
if len(self.val) > 10:

class X(object) and %-formatting. Opus 5.5 XHigh: 126 (object) bases; % is the main style in 20 of 36 problems (Medium: 23). Opus 4.6 and 4.7: 0 and 3.

Explicit __init__ call

GLM 5.3 · recli · ck8 · appctl.py:110-118
class CliError(Exception):
"""A fatal, user-facing error with the stream and exit code to use."""
def __init__(self, lines, stream="stderr", code=1, sensitive=False):
Exception.__init__(self, "\n".join(lines))
self.lines = list(lines)
self.stream = stream
self.code = code
self.sensitive = sensitive

Same file: class Runtime(object), class Config(object), class Param(object), 188 lines of %-formatting and 12 f-strings. GLM 5.3: 85 (object) bases in 10 problems.

Legacy typing imports

Fable 5.1 XHigh · xjq · ck5 · xjq.py:87-87
from __future__ import annotations
Fable 5.1 XHigh · xjq · ck5 · xjq.py:97-97
from typing import Any, Dict, List, Optional, Tuple, Union

from __future__ import annotations already allows dict[…] and list[…]; this file still uses Dict/List/Optional 31 times. Fable: 30 per 1k lines across 19 problems.

The same habits show up in libraries for other agents

These habits also follow agents into the libraries they write for other agents. On LibraryDesignBench, their libraries carry the same error-swallowing habits:

String equality fallback

GLM 5.3 · pyda (LDB library) · attempt 1 · src/pyda/windows.py:132-135
try:
return bool(a == b)
except Exception: # pragma: no cover - exotic types
return str(a) == str(b)

If comparing two values raises, a dataframe library compares their string forms, and keeps that path out of coverage. The same helper is copied into reconcile.py.

Ambiguous None return

Opus 5.5 · pyda (LDB library) · attempt 2 · src/pyda/io.py:545-557
def _matching_row_groups(f: FileInfo, arrow_filter) -> list[int] | None:
"""Row groups whose statistics may satisfy ``arrow_filter`` (None = all)."""
if arrow_filter is None:
return None
try:
if f.fs is not None:
dset = pds.dataset(f.path, format="parquet", filesystem=f.fs)
else:
dset = pds.dataset(f.path, format="parquet")
frag = next(iter(dset.get_fragments()))
return [rg.id for rg in frag.subset(filter=arrow_filter).row_groups]
except Exception: # statistics unusable (e.g. type mismatch) -> read everything
return None

Any error while reading Parquet statistics is turned into None, which callers already treat as “read every row group”.

Swallowed filesystem errors

GLM 5.3 · pyda (LDB library) · attempt 2 · src/pyda/io/fsutil.py:65-71
def path_exists(path) -> bool:
try:
fs = fs_for(str(path))
return fs.exists(str(path))
except Exception: # pragma: no cover
return False

Permissions, network or credential errors all read as “the file does not exist”, and the handler is excluded from coverage.

The Ugly

Agents skip what the library already does

The newest form of slop that is particularly concerning is the increasing scenario where an agent searches a library and finds the functionality it needs, then decides it was actually better all along to just implement it itself. It is infuriating, and it matters more every month: agents increasingly build on code other agents wrote, and every copy is another place for a bug to hide.

LibraryUseBench measures this directly. Each model gets a problem, a human-written library, and a minimal prompt that just says the library is available. A solution counts as a rewrite when it hand-writes something the library already provides; otherwise, it calls the library for that job.

With a minimal prompt, every agent re-writes what the library already does

LibraryUseBench: share of solutions that hand-write something the human-written library already provides

GPT-6 Luna
100%
GPT-5.6 Luna
93%
GLM 5.3 Flash
93%
Sonnet 5
87%
GPT-5.6 Terra
87%
Muse Spark 1.3
86%
DeepSeek V4 Flash
85%
Sonnet 5.5
80%
Opus 5
73%
Opus 5.5
73%
DeepSeek V4.1 Flash
73%
GPT-6 SOL
73%
One problem per LDB task (15 tasks), first attempt. Subagents read every solution and the library it was given; a rewrite names the library function that already does the job. Rates are averaged over tasks.

On LibraryUseBench, every model hand-writes something the library already does, in 73% to 100% of its solutions. Even the best ones do it in nearly 3 of 4 solutions written. It will become ever more important to solve this as agents increasingly work with agent written code over human written code.

LibraryDesignBench measures exactly this and highlights that, while agents do still write redundant functionality, it occurs in only 40% of their solutions. This is not, unfortunately, because agents somehow mastered software design for other agents. It is because for LDB, we have to use an incredibly prescriptive prompt. The same prompt with human designed libraries leads to ~17% reimplementation rate.

LDB also tells us whether agents write better or worse libraries than humans do. We measure that by how often the same three implementers, with the same prompt, have to work around the library they were given: patching a bug in it or rebuilding a piece that doesn’t fit the task.

On agent-written libraries, implementers fight the library more

The same 3 implementers and prompt, on the human library vs. on 8 agent-written ones

Worked around a broken or ill-fitting library

Human
27%
Agent-written
40%

Worked around, by who wrote the library

DeepSeek V4 Pro
56%human libraries 27%
GLM 5.3
53%
Grok 4.6
47%
GPT-6 Astra
40%
Opus 5.5
33%
Fable 5.1
33%
GPT-6 SOL
29%
Kimi K3
27%
One problem per LDB task (15 tasks), implementer attempt 1. Subagents read every solution and the library it was given; a workaround cites the library defect in its source. Rates are averaged over tasks.

Some examples of these rewrites:

Reimplemented SimPy scheduler

GPT-6 Luna (LUB) · simu one-shot-bell · solution · reentrant_one_shot_bell.py:41-46
def enqueue(kind, value):
nonlocal sequence
sequence += 1
# Urgent successor outranks all regular same-time work.
priority = 0 if kind == 'successor' and s['successor_priority'] == 'urgent' else 1
heapq.heappush(pending, (priority, sequence, kind, value))
SimPy (the library it was given) · simpy 4.1.1 · Environment.schedule · core.py:164-171
def schedule(
self,
event: Event,
priority: EventPriority = NORMAL,
delay: SimTime = 0,
) -> None:
"""Schedule an *event* with a given *priority* and a *delay*."""
heappush(self._queue, (self._now + delay, priority, next(self._eid), event))

It imports SimPy, uses it only to wait for the bell, then pushes (priority, sequence) onto its own heap. SimPy’s schedule already does exactly that. 8 of 12 models wrote their own queue here.

Reimplemented encoding detection

DeepSeek V4 Flash (LUB) · uglypie page-cleaner · solution · clean_pages.py:137-150
def find_declared_encoding(data):
window = data[:2048]
m = re.search(
rb"<\?xml(?:\s|\?)[^>]*\bencoding\s*=\s*[\"']([^\"']+)[\"']",
window,
re.I,
)
if m:
return m.group(1).decode("ascii", "replace").strip()
for mt in re.finditer(rb"<meta\b[^>]*>", window, re.I):
tag = mt.group(0)
m = re.search(rb"charset\s*=\s*[\"']?\s*([^\"'\s;>]+)", tag, re.I)
if m:
return m.group(1).decode("ascii", "replace").strip()
BeautifulSoup (the library it was given) · bs4 4.15.0 · EncodingDetector · dammit.py:692-700
@classmethod
def find_declared_encoding(
cls,
markup: Union[bytes, str],
is_html: bool = False,
search_entire_document: bool = False,
) -> Optional[_Encoding]:
"""Given a document, tries to find an encoding declared within the
text of the document itself.

It read bs4’s dammit.py, then wrote its own find_declared_encoding. All three implementers in LDB’s setup called bs4’s; all 12 LUB models wrote their own.

npr broadcast workaround

GLM 5.3 Flash on GLM 5.3's library · npr radiance-moments · solution · src/main.rs:290-292
// `npr`'s iterators walk a strided view in logical order, which is
// the scatter-add that `+=` on views cannot express for axes of
// length one.
GLM 5.3's npr library · npr (agent-written) · Zip · src/zip.rs:228-234
if <$t>::MUT && strides[$i].iter().any(|&s| s == 0) {
return Err(crate::error::NprError::Shape(
crate::error::ShapeError::Invalid(
"a mutable producer cannot be broadcast with repeated elements".into(),
),
));
}

The library’s += on a view rejects any axis of length one, which the spec allows for tiles. The implementer wrote its own element-by-element add instead, three times.

The same models that re-implement the most in SCB copy the most from human libraries in LDB. Opus 5.5’s and Fable’s libraries reuse about 30% of the production library’s public names, against 13% for GPT-6 SOL. They copy the interface and write their own implementation behind it.

Some code is aimed at other agents outright to target their training corpus:

BeautifulSoup = UglyPie

In LDB’s UglyPie problem, the designer writes an HTML parsing library to stand in for Beautiful Soup. One designer aliased its main class to BeautifulSoup, betting that implementers would reach for the name they already know from training.

What Now?

I might be biased, but clearly every new model that comes out should put SCB, LDB, and LUB on their model cards. And hire me after I defend in the spring.

Beyond these three benchmarks? We need longer horizon benchmarks, benchmarks where agents have to interact with each other, and benchmarks where agents need to consider how their decisions will impact future agents. Because agents will increasingly use artifacts designed by other agents and not humans.

Acknowledgements

Thanks to Prime Intellect for supporting this work through the Prime RL Residency, and Snorkel AI for supporting this through the Open Benchmarks Grant. Thanks again to Dex for running the SCB runs that enabled these results and to Sebastian Müller for the helpful feedback on the blog!

Citation

@misc{orlanski2026slopmovesup,
title = {Slop Is Moving Up the Stack},
author = {Orlanski, Gabriel},
year = {2026},
month = nov,
howpublished = {\url{https://gabeorlanski.github.io/posts/slop-moves-up/}},
note = {Blog post}
}


Next Post
Can Agents Design Libraries for Agents?