Git-flow
View as markdownGit-flow keeps two long-lived branches, main (released) and
develop (integration), and cuts three kinds of short branches off
them: feature/* off develop, release/* off develop toward main,
and hotfix/* off main. It trades the simplicity of trunk-based for an
explicit staging branch where work accumulates before a release.
Git-flow doesn't fit one board, because a feature and a release are
different lifecycles. Model it as two Shelbi Workflows: a feature
workflow whose issues branch off and merge back into develop, and a
release workflow that tracks the PR from develop into main. Hotfixes
reuse the shipped task workflow (the
feature-branch model) with main
as their base.
The board
Every card on the feature workflow cuts its branch off develop and
squash-merges back into develop. The On Develop column is "landed
on the integration branch," not "released." Shipping to main is a
separate release issue.
The statuses
Status identity lives once in the project's
status catalog,
workflows/statuses.yaml. Git-flow adds one status the shipped defaults
don't have: on-develop, the done-category landing for feature work
that's merged into develop but not yet released. It also adds staging
and released for the release workflow below. Declare them (id + name +
category) in the catalog first, then reference them by id. Keep all six
shipped statuses: the shipped task and subtask workflows reference them,
and if task (the project's default_workflow) can't load, the whole
project fails to load.
# workflows/statuses.yaml — git-flow statuses alongside the defaults.
statuses:
# shipped defaults
- { id: backlog, name: Backlog, category: backlog }
- { id: todo, name: Todo, category: ready }
- { id: in-progress, name: In Progress, category: active }
- { id: review, name: Review, category: handoff }
- { id: done, name: Done, category: done }
- { id: canceled, name: Canceled, category: archived }
# git-flow additions
- { id: on-develop, name: On Develop, category: done }
- { id: staging, name: Staging, category: active }
- { id: released, name: Released, category: done }The feature workflow
# workflows/feature.yaml — reference-only; identity lives in statuses.yaml
name: feature
description: Git-flow feature — branch off develop, merge back into develop.
git:
base_branch: develop # branches are cut off develop
branch: 'feature/{{id}}' # and named feature/<issue-id>
statuses:
- { id: backlog, owner: user, agent: orchestrator }
- { id: todo, owner: agent, agent: orchestrator }
- { id: in-progress, owner: agent, agent: developer }
- { id: review, owner: user, agent: review, tags: [review] }
- { id: on-develop, owner: user }
- { id: canceled, owner: user }
initial_status: backlog
transitions:
- from: in-progress
to: review
actions: [push_branch, open_pr] # PR base is develop (the workflow base_branch)
- from: review
to: on-develop
actions: [merge, delete_branch] # merge the PR into develop
- from: in-progress
to: canceled
actions: [close_pr, delete_branch]Setting git.base_branch: develop is the whole trick: branches are cut
from develop, the developer's hand-off rebases onto develop, and the
open_pr action targets develop because no transition overrides it with
a target:.
git.branch names the branches feature/<id>; without it they'd follow
the project's naming (<github-login>/<id> by default).
The merge strategy is not set here. The merge action always uses the
project's git.merge_strategy (squash by default), and ignores a
merge_strategy in a workflow's git: block.
The release workflow
A release issue tracks the PR that takes the accumulated develop into
main. Keep it human-driven. Shelbi only hands work forward
automatically from the in-progress status, and every git action
(push_branch, open_pr) runs in the worktree of the workspace the issue
is assigned to. A developer dispatched onto develop would also get
develop rebased onto main at hand-off, rewriting the long-lived branch
you mean to preserve. So the release workflow is plain status moves:
# workflows/release.yaml — reference-only; identity lives in statuses.yaml
name: release
description: Git-flow release — track the develop-to-main release PR.
git:
base_branch: main
statuses:
- { id: staging, owner: user } # you prepare the release and open the PR
- { id: review, owner: user } # the PR is open; you merge it on GitHub
- { id: released, owner: user }
initial_status: staging
transitions:
- { from: staging, to: review, actions: [] } # pure status moves: no
- { from: review, to: released, actions: [] } # branch, push, or merge---
id: release-2025-07
title: Release 2025.07
workflow: release
---When develop is ready, open the release PR yourself
(gh pr create --base main --head develop) and move the card to
Review. Merge the PR on GitHub with a merge commit (not a squash), so
main keeps develop's history and develop is never deleted, then move
the card to Released. With no merge edge, Zen Mode never merges a
release for you.
If you'd rather have Shelbi merge it, add actions: [merge] to the
review → released edge and set git.merge_strategy: merge in
project.yaml. That setting is project-wide, so feature merges into
develop become true merges too, and Zen Mode (which only lands squash
merges) stops merging for the project.
Orchestrator adjustments
Two routing rules keep the three lifecycles from colliding. Encode them in the orchestrator prompt (see Customize the orchestrator prompt):
## Routing rules — git-flow
- **Features** default to the `feature` workflow (base `develop`). Ordinary
work goes here; do not route feature work at `main`.
- **Hotfixes** (titles prefixed `hotfix:`) use the shipped `task`
workflow (base `main`) so the fix branches off the released line. After a hotfix
merges to `main`, open a follow-up issue to merge `main` back into
`develop` so the branches don't diverge.
- **Releases** are created by the user on the `release` workflow. Never
dispatch or move a release card: cutting one is a human decision about
what's ready to ship.The develop-back-merge after a hotfix is the one bit of bookkeeping git-flow needs that a single workflow can't express. Surface it as a follow-up issue rather than trying to encode it as a transition.
See also
- Workflows: parameterization
—
{{var}}inbase_branchif you run several concurrent integration branches instead of onedevelop. - Workflows: the
branch:task field: how branch names are resolved, and how to point an issue at an existing branch. - Feature-branch — the simpler model hotfixes fall back to.