I used to tell my coding agent something like this:
Update
develop/1.65.0. Createfeature/<task key>from it. Implement the task. Then make an MR intodevelop/1.65.0.
Tomorrow the same release could already be in alpha, and a bit later in beta. So I had to know the current stage of every release myself and pass the right branches to the agent every time.
The release stage was the one piece of my release process that lived outside Git, CI and Jira: in people's heads and in a Confluence table I updated by hand. This post is about how I made it computable, first for myself in a portal, then for the agent as an MCP tool, and where the algorithm can be wrong.
Some context. In my Git Flow every release has its own chain of branches, stage/version:
develop/1.65.0 → alpha/1.65.0 → beta/1.65.0 → prod/1.65.0
Several versions live at the same time: one in development, one in testing, one in beta, one in production. Fixes made in an older version must be carried into newer ones. The model is described in part 1, and the CI checks built on top of it in part 2.
Branches don't tell you the stage
The obvious algorithm, "find the latest existing branch", does not work. When a new release is set up, the branches of all its stages are created at once. prod/1.65.0 exists from day one, even if the release has only just started being developed.
A different signal is needed: the action by which, according to our process, a release is moved forward. For us that action is a merged merge request into the branch of the corresponding stage. The target branch is named <stage>/<version>, and the MR is linked to a Jira task. If a matching MR was merged into alpha/1.65.0, the release reached alpha. If such an MR later appeared in beta/1.65.0, it reached beta.
The tool finds merged MRs with a Jira key in the title, branch name or description, matches their target branches against the release and the order of stages, and picks the latest stage that has such evidence:
Release 1.65.0
Branch Merged MR as evidence?
develop/1.65.0 yes
alpha/1.65.0 yes ← current stage
beta/1.65.0 no
prod/1.65.0 no
Result: stage = alpha, baseBranch = alpha/1.65.0
This is not a universal property of Git. It is our definition of a release state, and it works because the process was agreed in advance. If your team moves stages in a different way, you need different evidence.
I am stating this definition in hindsight. At first I only wanted to get rid of the table. The formal algorithm and its limits took shape while I was building the tools.
The agent side: an MCP that answers "where do I start?"
I did not want the agent to guess the release state from a local checkout, pick the default branch, or memorise each project's quirks in a prompt. It needed a tool that answers one concrete question: where do I start work on this Jira task, and where do I send the MR afterwards?
That tool is an MCP server I called Delivery Manager. The name can mislead: it does not replace a real delivery manager. A human still plans releases and sets the Fix Version in Jira. The MCP receives a task that is already planned and works out how to handle it within the existing release process.
Given the project, the version and the task key, it queries GitLab, determines the current stage and returns parameters for the work. Schematically:
# Fictional example; a shortened illustration of the contract,
# not the literal JSON response of the MCP.
fixVersion: 1.65.0
currentStage: alpha
baseBranch: alpha/1.65.0
baseBranchExists: true
featureBranch: feature/DEMO-123
mergeRequest:
targetBranch: alpha/1.65.0
The agent does not need to understand why 1.65.0 is on alpha today. It gets a verified baseBranch and the MR target branch.
The rule I gave the agent is hard: it does not choose the base release branch on its own. The only source is the release MCP. Work does not start by guesswork if:
- the task has no Fix Version;
- the current stage cannot be determined;
- the needed release branch is missing;
- GitLab is unavailable;
- the task is not in the "To Do" status, so it cannot be considered prepared for implementation.
The last one fired in a quite down-to-earth case: I forgot to move a task to the right status myself, and the work stopped. That is not a tool error. It refused to bypass the process just to start writing code.
Where the algorithm came from: a portal instead of a Confluence table
The algorithm did not start in the MCP. Before that I had a Confluence table: rows are release numbers, columns are stages and operations. When a release moved on, I wrote in the date, removed the green fill from one cell and painted another. Its accuracy depended on me, while GitLab had already recorded every action I was copying by hand.
I am an Android developer and had never written web portals. I discussed the idea in ChatGPT, got an implementation prompt and handed it to a coding agent. That is how my gitlab-panel portal started. It builds the picture from GitLab data: a matrix with versions as rows and stages as columns.
The matrix shows two directions of change flow. Horizontally, changes go through the stages inside one release. Vertically, the portal checks the rule "fixes from an older release must reach the newer one": it compares the branches of neighbouring releases and finds commits present in the older version but absent from the newer one, with merge commits filtered out. That rule used to be just an obligation of the team.
From an arrow in the matrix the portal can also start a "pour", our word for transferring changes between stage branches or between releases. I confirm the source, target and changes; the portal uses an existing Jira task or creates a sub-task, moves it to CodeReview (and stops if that fails), creates a temporary feature/<KEY> from the source branch and an MR into the target. Squash is deliberately off, so the commit history can later be used to check how commits moved between releases. The portal also tries to enable merge after a successful pipeline, but that is not guaranteed; some steps may need attention.
The portal does not decide whether it is time for a release to move to the next stage. A tester reports readiness, a delivery manager plans further progress and creates the pour task. What is automated is the actions after the decision.
Two implementations, no shared library
The portal and Delivery Manager are separate projects with separate codebases. I did not make the MCP depend on a running portal.
When the MCP was needed, I asked the agent working on the portal to explain where exactly in the source the stage-detection algorithm lives: files, methods, logic. I handed that note to another agent, the one building the MCP, and it carried the domain idea over.
So there are now two implementations that rely on one process and on the GitLab history, but there is no automatic code synchronisation between them. Stage order is another compromise: in the MCP it is established from the project's release branches and known stage names, then fixed in the space settings; in the portal there are separate screens for it.
Where it can be wrong
The computation rests on three assumptions:
-
The stage is the furthest confirmed transition. A merged MR into
beta/<version>means the release reachedbeta. The algorithm does not try to reconstruct business logic from task names. -
A release moves only forward. My model has no rollback from
betatoalpha, so the latest confirmed stage stays current. If your team can officially roll stages back, this algorithm is not enough. - No confirming MRs means the first existing stage. That is the normal state of a freshly created release: all branches exist, no pour has happened yet. It is a deliberate fallback, not a proven transition.
There are engineering limits too: two implementations mean a rule change has to be ported between them by hand. The stage check did not become part of the old jira:check CI script from part 2; other tools do it now. And I did not measure how many hours this saved, so I will not quote percentages.
What changed is easy to describe without numbers. I used to remember the release stage, update the table, explain branches to a person or an agent and run a sequence of actions in GitLab. Now the state comes from history, the portal shows discrepancies, and the agent asks the MCP instead of asking me.
What I would reuse
-
Pick the event in Git that counts as evidence of a stage in your process. For me it is a merged MR with a task key into
<stage>/<version>. Branch existence is not evidence if branches are created up front. - Decide explicitly what happens when there is no evidence yet and which movements your algorithm does not model, such as rollbacks.
- Give the agent a tool that answers "from where and to where", instead of putting branch names in the prompt.
- List the stop conditions. If the tool cannot answer with justification, the right action is to stop, not to guess.
- Keep the decision to move a stage with people. Automate what follows the decision.
The full case study, including the pour wizard and the two-directions diagram, is on my site: https://hram.github.io/en/articles/hram-git-flow-ai/




Top comments (1)
The "no shared library" part is where I'd expect the first silent bug. Two implementations of the same stage rule will drift the day someone adds a stage name or changes how the Jira key is matched, and nothing fails, the portal and the agent just start disagreeing.
A cheap guard that keeps them separate: one fixtures file (fictional releases, branches, merged MRs, expected stage and baseBranch) checked into both repos, with a test in each that runs its own implementation against it. A rule change then means editing the fixture, and the side you forgot goes red in CI.
Two edge cases worth a fixture row each: a merged MR that was later reverted (still counts as evidence under rule 1), and a hotfix merged straight into prod/ while beta has no MR yet.
Does Delivery Manager return the MR ids it used as evidence? Having the agent paste them into the new MR description would make a wrong stage easy to spot in review.