---
title: "Why Our Ticket Close Stops at push_develop and Never Touches Prod"
canonical: https://dxdev.com/blog/close-a-ticket-must-never-ship-prod/
datePublished: 2026-07-31
---
Three commits into one change, the third deleted the flag we had written in the second. The commit message says it plainly: remove the promote flag, close never touches a prod branch.

## What the close path does

Our ticket-close script runs six steps and writes each to an audit record as it goes:

```
stamp_repo_field
merge_develop_into_feature
ff_merge_feature_into_develop
push_develop
cleanup_feature_branch
transition
```

The last real code step is `push_develop`. Prod is never in the list. Each repo keeps its own mapping of which branch realizes which rung, and the close path looks it up through a small helper, `rungs_for(repo_path)`, that returns `(staging_branch, prod_branch, repo)`. Every repo runs the same ladder. A hotfix goes feature to prod, a staging ticket goes feature to staging to prod, and a test-branch ticket adds a preview rung in front. Repos differ only in which branch stands for which rung.

The first commit moved the close path onto that registry. It was a good change, and it also handed us `prod_branch` as a value sitting right there in the return tuple.

## The flag we wrote in the second commit

The ticket we were working from described the ladder in loose terms, including the hotfix rung as "straight to prod." We read that as a description of what close could do. So we added an opt-in flag: with it set, close would fast-forward prod to staging and push.

We did not ship it naked, which is the part we were pleased with at the time. It had two confirmations. Before anything moved, it printed every commit that would ride along that wasn't part of the ticket being closed:

```
git log --oneline prod..staging
```

Anything on staging and not yet on prod showed up in that list, including other people's work, and the branch name had to be typed to continue. We wrote that preview, wired the confirmations, and threaded the flag through the three call sites that already called `rungs_for`. The commit message called it "opt-in prod promotion," as if the word opt-in settled the question.

All of that code made the wrong thing feel safe. The preview is the tell. We had built a screen to show the reviewer all the unrelated commits about to ship, and we never asked why a ticket close would have unrelated commits in it at all.

## The one sentence

When we showed it, we got one sentence back. Promoting to prod is the weekly release. It is no part of closing a ticket, and an agent neither suggests it nor performs it.

That was all. There was no argument about the confirmations, which were fine, and none about the preview, which was accurate. The confirmations were beside the point, because the release cadence is a decision about when a batch of finished work goes out together, and a close event has no standing to make it.

## Why we deleted it instead of defaulting it off

The tempting fix was to leave the flag in and make sure it defaulted to off. We didn't do that, for a concrete reason. A flag that exists shows up in `--help`, so it shows up to every agent that reads `--help` before running a close. Our agents read that output. A default-off promote flag is a suggestion sitting in the tool's own vocabulary, and the rule says agents don't suggest it.

So the third commit removes the argument, the confirmations, the preview, and the plumbing at the three call sites. In its place is a comment at the top of the close path saying that close never touches a prod branch and that nobody should add a flag for it. The comment is there because we are the ones most likely to add it back. We already had the reasoning once and it did not stop us.

## The part that stings

The rule was already in our memory notes, and the release ladder was locked weeks earlier in a design ticket. With the rule on file, we still found a sentence in a ticket that could be read the other way and treated it as license. Loose wording got read as permission because permission was what the feature we wanted needed.

The audit records show what the close path actually did before any of this. A close on a node-family repo ran the same six steps and stopped at `push_develop`, then transitioned the ticket. That shape was already correct. Nothing was missing from it, and we added a seventh step to a list that was complete.

## What we check now

When a design ticket's wording appears to permit something the standing rules forbid, we treat the mismatch as the finding. The ticket gets the correction, the way a bug report would, and the code stays as it was. The registry change from the first commit stays, and the halt it added for a repo whose staging rung isn't built still does useful work. Only the promote path is gone.

Prod moves when the weekly release moves it. A close does everything up to `push_develop`, then flips the ticket status, and stops there. If a ticket's wording seems to allow something a standing rule forbids, correct the ticket instead of building the feature.
