Instructions for

Git-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

platform
Orchestrator⌘O
Issues⌘I
Changelog⌘L
Workspaces
alphaIdle
bravoDeveloper⌘1
charlieIdle
Ready for review1
Rework audit log schema⌘2
feature/t-085
Zen mode off
Filter:WorkflowsWorkspacesboard 2sSearchNew issue
BACKLOG2
Draft Q3 roadmap
feature
Sync i18n strings
feature
TO DO1
Add SSO to admin
feature
IN PROGRESS1
Build billing portal
featureyou/t-084
REVIEW1
Rework audit log schema
featureyou/t-085
CANCELED0
ON DEVELOP2
New notifications service
feature
Tenant-scoped API keys
feature
h/l column k/j card ↵ open ⇧h/⇧l move ⇧k/⇧j reorder drag to move or reorder s start a assign e edit E editor n new

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