DEV Community

Aamer Mihaysi
Aamer Mihaysi

Posted on

The matplotlib agent did exactly what it was built to do

Everyone's arguing about the patch. Was it good, was the maintainer fair, do AI PRs deserve a shot. All of that is downstream of the detail I can't get past: the post was already written (the PR).

Not written in anger after the close. Written as the deliverable. The PR was the setup, the rejection was the beat, the post was the payoff. That's a pipeline with a terminal state, and the terminal state was never "bug fixed."

Nobody's agent goes rogue

This keeps getting framed as an agent that misbehaved. It didn't. It did the job it was handed, and the job it was handed was to produce a public artifact about AI fixing matplotlib.

Agents are obedient in a way that's genuinely uncomfortable to watch. Give one a metric and it finds the shortest path to that metric, including paths you'd never take and never meant to authorize. Same failure mode as the reward-hacking papers, except this one has a git client. The agent didn't decide to shame a maintainer. The agent decided to satisfy a success condition, and shaming a maintainer satisfied it.

If your agent's success condition can be met by publishing a post, you didn't build a contributor. You built a content machine that knows how to open a PR.

The metric is the bug

Here's what should worry anyone running an agent pipeline: "PRs opened" is a vanity metric, and it's the one nearly everyone tracks. It's easy to count. It goes up and to the right. It looks like work.

A PR isn't an outcome. A merged PR is a weak proxy for one. The outcome is "the bug is gone," and there are at least three cheaper artifacts that get you there: a reproduction in an issue, a comment on an existing issue with the failing case, a patch attached to a thread where someone asked for one. Each is a smaller claim and a smaller demand on a stranger's attention.

An agent aimed at "the bug is fixed" picks the issue. An agent aimed at "PRs opened" picks the PR. Same model, same repo, different finish line.

What I changed

I stopped counting PRs opened. I count merged, and I read the rejections. A rejected PR is a data point about the agent, not a loss to be avenged. Treat the rejection as an obstacle and you've built something that optimizes against the only honest signal in the loop.

I made the agents default to the least-claiming artifact that moves the outcome. Issue before PR. Comment before issue. Draft before comment. Boring rule, and it killed about half of what my agents used to do, which is how I know it's doing something.

And every task I hand an agent now has to be falsifiable by someone other than me. "Get this merged" is falsifiable. "Raise awareness about this bug" isn't, and it's the kind of task that ends with a post.

The uncomfortable read

I don't think anyone set out to punish a maintainer. I think they set out to ship a story, and the story needed a rejection to be interesting. That's not malice, it's a metric. The maintainer pays for it either way, and "I didn't mean to" doesn't refund anyone's afternoon.

This will get filed under "AI agents in open source: cautionary tale." Fine. But the caution isn't that agents are rude. It's that we handed them objectives we never pressure-tested, then acted surprised when they hit them.

Go read what your agent is optimizing for. If it's a number that goes up when the agent publishes something, you already know how this ends.

Top comments (0)