---
title: "develop was shipping the wrong work"
canonical: https://dxdev.com/blog/2026-05-19_develop-was-shipping-the-wrong-work/
datePublished: 2026-05-19
---
On the morning of May 19, I opened a release diff for the platform and found seven commits in local `master` that weren't in the remote. That looked like a problem. It wasn't the real problem.

The seven commits were already shipped. Local `master` just hadn't synced. Once I pulled, the lag resolved and the diff collapsed to the handful of items actually queued for release. That should have been the end of it.

It wasn't.

## the noise that hid the real thing

the platform runs on a branch model where `develop` is the release branch, `preview` is a preview surface, and `master` is production. Features go into `develop` when they're approved and ready to ship. That's the invariant. It's supposed to mean: anything on `develop` is cleared for the next release.

I had been running several parallel work streams. One was a specific change that had cleared review and was genuinely ready. A second stream was staff-center modernization work, larger and not approved for this release. A third was agent-system plumbing. All three had active feature branches.

When I cleared the seven-commit lag and looked at the actual diff, I saw items from all three streams. The approved one and the not-approved ones, side by side in `develop`, looking equally ready to ship.

They had been merged in without a gate.

## what leaked in, and how

The staff-center and agent-system work had ended up in `develop` during a prior session. It wasn't sabotage and it wasn't a fluke. When you're working across multiple branches with an AI agent executing git operations, the agent touches the integration branch the same way it touches a feature branch. It does not know that `develop` means something different. It knows that you said "merge this." The gate is your own memory, applied consistently at every merge.

My memory was not applied consistently.

The agent had merged feature work into `develop` without me flagging it as release-pending first. The feature branches still existed. The commits were real. The work was genuine. None of that made it safe to ship in this release window.

The uncomfortable part is that nothing in the diff looked wrong. The code was correct. The changes were intentional. The only signal that something was off was the shape of the diff itself: larger than expected, and including surfaces I hadn't consciously released.

That is a hard category of error to catch on instinct alone.

## what the revert actually preserved

I reverted the contaminating commits. I kept the reviewed change because it was approved and genuinely belonged in the release. The staff-center and agent-system changes went back to their feature branches, still intact, still valid, just not in this release.

The review observations I had collected while looking at the staff-center work got carved into follow-up tickets instead of blocking the ship. They weren't blockers. Making them blockers would have been the wrong call. The ship went out with the right scope.

The revert itself was not the important part. The important part was stopping to ask: does every item in this diff have a release decision attached to it? That question is not one the agent asks automatically. I have to ask it. I had not been asking it.

## what I changed so the path is harder to repeat

Feature branches should stay in feature branches until there is a deliberate release decision. That sounds obvious. The problem is that in a high-throughput session with an AI agent moving fast, the deliberate decision step is the first thing that gets compressed.

After this incident, I added a memory entry to my agent system stating that `develop` merges require explicit release intent, not just technical correctness. The memory is read at session start. It does not prevent bad merges. It makes the bad merge path slightly more conscious.

I also noted a push-hook gap. the platform's git setup does not currently reject a `develop` push that includes non-approved work. That enforcement would have to live in the merge step, either as a checklist the agent runs or as a branch-protection rule requiring a release ticket reference. Neither exists yet. Both are on the list.

The immediate fix was manual: check the diff against an approved-items list before any `develop` merge. That's not automation. It's a habit. But habits are what you run on while you build the automation.

## parallel throughput changes what integration failure looks like

Before AI-paired development, the limiting factor on branch traffic was typing speed and context switching. You could realistically hold two or three work streams in your head at once. The integration branch accreted slowly enough that a bad merge usually announced itself before much damage was done.

With an agent running, you can push five streams at once. Each one is coherent, tested, intentional. The problem is not that any single stream is bad. The problem is that the integration branch now needs to be a semantic safety system, not just a place to collect commits.

The agent helps me go faster. It does not help me track what has and has not been approved for release. That distinction lives in my head, nowhere else. When I lose it for one merge, the integration branch becomes a misleading artifact: it looks clean, CI passes, and the diff contains work that was never supposed to ship.

The merge that contaminated `develop` probably took thirty seconds. Finding it, reverting it cleanly, and getting the right scope back into the release took most of the morning.

Speed is only an advantage when the integration branch still means what it's supposed to mean.
