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 tasks branch off and merge back into develop, and a
release workflow whose task PRs develop into main. Hotfixes reuse
the feature-branch default 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 task.
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:
# 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: 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 # feature/* branches cut off develop
merge_strategy: squash
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: orchestrator }
- { 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] # squash 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 and the open_pr action targets develop because no
transition overrides it with a target:.
The release workflow
A release task PRs the accumulated develop into main. Pre-fill its
branch: so the orchestrator operates on develop
directly instead of cutting a new branch:
# workflows/release.yaml — reference-only; identity lives in statuses.yaml
name: release
description: Git-flow release — PR develop into main and tag.
git:
base_branch: main
merge_strategy: merge # a true merge preserves develop's history
statuses:
- { id: staging, owner: agent, agent: developer }
- { id: review, owner: user, agent: orchestrator }
- { id: released, owner: user }
initial_status: staging
transitions:
- from: staging
to: review
actions: [push_branch, open_pr] # open PR: base main, head develop
- from: review
to: released
actions: [merge] # merge develop into main; keep develop---
id: release-2025-07
title: Release 2025.07
workflow: release
branch: develop # operate on develop as-is; don't cut a branch
---Note merge_strategy: merge (not squash) and the absence of
delete_branch: develop is long-lived, so the release preserves its
history and never deletes it.
Orchestrator adjustments
Two routing rules keep the three lifecycles from colliding. Encode them in the orchestrator prompt (see Author a custom workflow):
## 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 `default` workflow
(base `main`) so the fix branches off the released line. After a hotfix
merges to `main`, open a follow-up task to merge `main` back into
`develop` so the branches don't diverge.
- **Releases** are created by the user on the `release` workflow with
`branch: develop` pre-filled. Never auto-promote a release — 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 task 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 a release task operates ondevelopwithout cutting a new branch. - Feature-branch — the simpler model hotfixes fall back to.