17 KiB
Branching Strategies
A menu of common branching strategies, what they look like, when to use them, and how AIDLC's Construction worktrees map onto each. When the orchestrator dispatches aidlc-pipeline-deploy-agent at Bolt boundaries, this file is the menu the agent surveys to map a team's affirmed branching strategy onto the aidlc-worktree tool's flags.
Reading practices: see
knowledge/aidlc-shared/rules-reading.mdfor empty-template detection, semantic-topic matching, and the active-spaceproject.md → team.md → org.md → hardcoded defaultsfallback chain. In the runbooks below, those files live underaidlc/spaces/<active-space>/memory/.See also
cicd-patterns.md§ "Branch Strategies" for the higher-level CI-flow context.
Trunk-Based Development (default)
main ────────────────────────────────────────►
▲ ▲ ▲ ▲ ▲ ▲
│ │ │ │ │ │ short-lived feature branches
│ │ │ │ │ │ (1-2 days max), squash-merge to main
bolt-1 bolt-3 bolt-5
bolt-2 bolt-4
Shape. All work merges to main via short-lived feature branches. Long-lived branches don't exist. Feature flags gate incomplete work in production.
When to use. Default for most teams. Especially when CI pipeline duration is short (under 30 minutes) and observability is good enough to detect production issues quickly.
Common problems.
- Teams with infrequent releases find it hard to "hold" features for a release window. Feature flags are the answer, not branches.
- Teams without good test coverage shouldn't trunk-base — every commit hits production-shaped pipelines, so flaky tests block everyone.
Worktree mapping. Create: aidlc engine worktree create --slug <bolt-slug> --base main. Merge: --target main --strategy squash. Each Bolt = one squash commit on main.
Parallel Bolts. Cleanest fit. Multiple Bolts can be in flight simultaneously; each branches from current main, each merges back without rebase contention because squash flattens history at merge time.
Execution runbook
When dispatched for trunk-based:
- Read
## Way of Workingfrom the active space'sproject.md,team.md, thenorg.mdpershared/rules-reading.md; use hardcoded defaults only if all three are empty. - Resolve flags:
--base main --target main --strategy squashfor the default; deviate only ifteam.mdexplicitly says otherwise. - Create: invoke
aidlc engine worktree create --slug <bolt-slug> --base main. - Merge (after Bolt gate approval): caller must be on
mainat the main checkout. Invokeaidlc engine worktree merge --slug <bolt-slug> --target main --strategy squash --message "<commit message>". - Return the JSON envelope per § Response contract back to the orchestrator.
Failure modes
- Dirty tree on merge. Local uncommitted changes on
main; tool errors with the git message verbatim. Orchestrator's halt-and-ask offers retry/abort. Worktree preserved on retry; user explicitly discards on abort. - Conflict on squash. Squash conflicts with concurrent
mainmotion (e.g. another Bolt landed first). Tool exits non-zero with{status: "conflict", conflict_files, detail}. Orchestrator quotesdetailto the user. - Branch already exists. Pre-audit error; the tool refuses to clobber. Orchestrator should run
discardfirst (rare) or pick a different slug.
GitHub Flow
main ────────────────────────────────────────►
▲ ▲ ▲ ▲
│ │ │ │ feature branches with PRs
│ │ │ │ (no time limit; can live longer than 1-2 days)
feat-A feat-B feat-C feat-D
Shape. main + indefinite feature branches. PRs merge to main. Branches can live longer than trunk-based — days to weeks for larger features. No develop or release branches.
When to use. Teams that want trunk-based discipline but need longer-lived feature branches. Open-source projects often use this.
Common problems.
- Branches that live too long accumulate merge debt. Discipline required to either land or close.
- Without feature flags, in-flight features block release of unrelated work.
Worktree mapping. Same base/target as trunk-based: --base main --target main. Strategy is usually squash, but teams that prefer to preserve the branch in history use merge. Team picks at affirmation; agent reads team.md.
Execution runbook
When dispatched for GitHub Flow:
- Read
## Way of Workingfrom the active space'sproject.md,team.md, thenorg.md(including any merge-style statement). The merge-strategy choice (squash vs merge) is what differs from trunk-based. - Resolve flags:
--base main --target main --strategy <squash|merge>per affirmation; default tosquash. - Create:
aidlc engine worktree create --slug <bolt-slug> --base main. - Merge:
aidlc engine worktree merge --slug <bolt-slug> --target main --strategy <squash|merge> [--message "<msg>"]. With--strategy merge, a no-fast-forward merge commit preserves the bolt branch's individual commits. - Return per § Response contract.
Failure modes
- Same as trunk-based, plus:
- Stale base for
--strategy merge. Long-lived bolt branches against a movingmainproduce conflicts. The tool reports the conflict envelope; the user resolves in the worktree (preserved on conflict) and re-invokes merge.
GitFlow
main ────────────────────────────────►
▲ ▲
│ │ release/v1.0 release/v1.1
develop ─────┴───────────┴────────────────────►
▲ ▲ ▲ ▲ ▲
│ │ │ │ │ feature branches off develop
│ │ │ │ │
feat-A feat-B feat-C feat-D
▲
│ hotfix/v1.0.1 (off main, merged to both)
Shape. Two long-lived branches (main = production, develop = integration), plus feature/*, release/*, hotfix/* short-lived branches. Releases cut from develop → release/* → main with version tags.
When to use. Teams with strict release management — quarterly releases, regulated deployments, stable production while integration continues. Common in enterprise + financial services.
Common problems.
- Long-lived
developaccumulates merge debt againstmainover a release cycle. Painful merges at release-cut time. - Hotfixes require dual-merging (to both
mainanddevelop) — easy to miss the second merge.
Worktree mapping. Feature Bolts: --base develop --target develop. Hotfix Bolts: --base main --target main (and the operator merges to develop separately — out of scope for aidlc-worktree). Strategy is usually merge to preserve branch history; squash is also valid.
Execution runbook
When dispatched for GitFlow:
- Read the active space's
## Way of Working. Look for the integration-branch name (developis the convention; teams sometimes useintegrationornext). - For feature Bolts:
--base <integration> --target <integration> --strategy <merge|squash>. Default tomerge. - For hotfix Bolts (rare in Construction; usually triggered by an out-of-band stage):
--base main --target main --strategy merge. The operator separately merges the hotfix back to<integration>afteraidlc-worktree mergesucceeds. Out of scope for the tool. - Create:
aidlc engine worktree create --slug <bolt-slug> --base <integration>. - Merge: caller must be on
<integration>at the main checkout.aidlc engine worktree merge --slug <bolt-slug> --target <integration> --strategy <merge|squash>. - Return per § Response contract; if hotfix, include
notes: "manual merge to <integration> required"so the orchestrator surfaces the follow-up.
Failure modes
<integration>branch missing locally. Pre-audit error; tool refuses to invent the branch.- Wrong cwd on merge. Defensive HEAD check fails:
expected branch <integration>, found <actual>. Caller mustcdto the main checkout andgit checkout <integration>first. - Hotfix merge to second target forgotten. Out-of-scope for
aidlc-worktree; orchestrator's aidlc-pipeline-deploy-agent dispatch should always include the second-target reminder innotes.
Release Branches
main ────────────────────────────────►
▲ ▲
│ │
release/v1.0 ┴──── (frozen for stabilisation) ──────►
▲ ▲
│ │ bug fixes only on release branch
│ │
fix-A fix-B
│
└──► merge release/v1.0 → main + tag v1.0.0
Shape. Trunk-based or GitHub Flow on main, with a release branch cut at code-freeze. Stabilisation work (bug fixes only) happens on the release branch; new features continue on main.
When to use. Teams shipping versioned software where release stability matters more than continuous deployment — desktop apps, embedded software, enterprise products with hard release dates.
Common problems.
- Bug fixes on release branch must be cherry-picked or merged back to
mainso they don't regress in the next release. - Long stabilisation periods can block feature work waiting for the release branch to merge back.
Worktree mapping. Bolts on main use --base main --target main. Release-branch fix Bolts use --base release/vX.Y --target release/vX.Y. Strategy is merge typically (preserves the fix branch in history for traceability).
Execution runbook
When dispatched for Release Branches:
- Read the active space's
## Way of Working. Look for the release-branch pattern (release/vX.Yis the convention). - Determine which line the Bolt belongs to from the Bolt's metadata (the orchestrator passes a
target_line: main | release/vX.Yhint). Default tomainwhen ambiguous. - Create:
aidlc engine worktree create --slug <bolt-slug> --base <line>. - Merge: caller on
<line>at the main checkout.aidlc engine worktree merge --slug <bolt-slug> --target <line> --strategy merge. - If the Bolt was a release-branch fix, include
notes: "consider cherry-pick to main"in the response — the operator handles the cross-merge. - Return per § Response contract.
Failure modes
- Release branch missing locally. Pre-audit error.
- Bolt targeted release branch but main has diverged. Bolt completes; orchestrator surfaces the cherry-pick reminder via the
notesfield. - Same as GitFlow for wrong-cwd / dirty-tree / conflict cases.
Monorepo
main ────────────────────────────────────────►
▲ ▲ ▲ ▲
│ │ │ │ feature branches with path-based scope
│ │ │ │
pkg-a/feat-1 pkg-b/feat-2 pkg-c/refactor shared/lib-update
Shape. Single repo holding multiple packages/services. Branches scoped by path (changes within packages/auth/ are one Bolt; changes spanning packages need explicit cross-package coordination). Can run trunk-based, GitHub Flow, or any of the above on top.
When to use. Teams with multiple closely-coupled services that benefit from atomic cross-service changes. Tooling support required: Nx, Turborepo, Pants.
Common problems.
- CI must be path-aware (only test packages with changes). Monolithic CI defeats the purpose.
- Cross-package changes can't be parallelised cleanly — they require coordinated merges.
Worktree mapping. Same as the underlying strategy (trunk-based default). The path-awareness lives at the CI/test layer, not the worktree layer. Strategy is usually squash per package change.
Execution runbook
When dispatched for Monorepo:
- Resolve the underlying strategy (trunk-based default) per its runbook above.
- The Bolt slug should encode the package scope (e.g.
auth-token-rotationrather thanfeature-1) sogit worktree listoutput stays diagnosable. - Create + merge identical to the underlying strategy.
- Return per § Response contract.
Failure modes
- Cross-package Bolts. When a Bolt's units span two packages, the merge succeeds but the cherry-pick / coordinate-with-other-package reminder is the operator's job. Surface in
notesif known. - Same as the underlying strategy.
Response contract
When the orchestrator dispatches aidlc-pipeline-deploy-agent for a worktree create or merge, the agent invokes aidlc-worktree directly and reports the JSON envelope below back to the orchestrator. SKILL.md Step 0.5 / Step 6.75 then call aidlc-worktree verify as a deterministic backstop confirming the audit event landed.
Create response (success)
{
"emitted": "WORKTREE_CREATED",
"slug": "<bolt-slug>",
"worktree_path": "/abs/path/.aidlc/worktrees/bolt-<slug>",
"branch": "bolt-<slug>",
"base": "<base-branch>",
"audit_timestamp": "2026-05-18T12:34:56Z",
"notes": "<optional follow-up reminders for the orchestrator>"
}
Merge response (success)
{
"emitted": "WORKTREE_MERGED",
"slug": "<bolt-slug>",
"worktree_path": "/abs/path/.aidlc/worktrees/bolt-<slug>",
"target": "<target-branch>",
"strategy": "squash",
"commit_sha": "<sha>",
"audit_timestamp": "2026-05-18T12:34:56Z",
"notes": "<optional follow-up reminders>"
}
If a merge error carries [merge-succeeded:<sha>] and says the
SWARM_SOURCE_MERGED post-result audit row failed, the Git merge already
landed but no aggregate source authority exists. Preserve the worktree and do
not retry the same merge command. Restart the stage attempt, or use
AIDLC_SKIP_SOURCE_FRESHNESS=1 only after explicit human approval.
Merge response (conflict)
{
"status": "conflict",
"slug": "<bolt-slug>",
"worktree_path": "/abs/path/.aidlc/worktrees/bolt-<slug>",
"conflict_files": ["src/foo.ts", "src/bar.ts"],
"detail": "Merge produced conflicts in worktree at <path>. Worktree preserved for inspection."
}
The orchestrator's halt-and-ask quotes the detail field verbatim. See aidlc-common/protocols/stage-protocol-construction.md § "Halt-and-ask on failure" and skills/aidlc/SKILL.md § "Halt-and-ask failure handling" for the full prompt shape and preservation invariant.
Discard response
{
"emitted": "WORKTREE_DISCARDED",
"slug": "<bolt-slug>",
"worktree_path": "/abs/path/.aidlc/worktrees/bolt-<slug>",
"reason": "agent-discard",
"audit_timestamp": "2026-05-18T12:34:56Z"
}
If the worktree was already gone (idempotent path), emitted is null and reason is already-discarded — no audit event is re-emitted.
How AIDLC reads strategy from team practices
The dispatch protocol described in this section is implemented by SKILL.md Step 0 (worktree create) and Step 6.5 (worktree merge). aidlc-bolt complete --merge orchestrates around the dispatch (forkState merge-back, forkAudit merge-back) but does not call aidlc-worktree merge directly — the dispatch lives in SKILL.md prose.
When a Bolt starts (Step 0) or completes (Step 6.5), the orchestrator dispatches a Task call to aidlc-pipeline-deploy-agent with two inputs:
- The resolved
## Way of Workingstatement fromaidlc/spaces/<active-space>/memory/{project,team,org}.md(fallback chain inshared/rules-reading.md). - The Bolt's metadata (slug, source branch, optional target-line hint for release-branch teams).
The agent reads this file (branching-strategies.md) as the menu, matches the team's stated strategy to one of the five above, picks the right aidlc-worktree flags, invokes the tool, and returns the response envelope per § Response contract.
If the team's stated strategy doesn't map cleanly to the menu (e.g. "we use a hybrid"), the agent picks the closest fit and notes the deviation in the response's notes field; the orchestrator surfaces it in the audit log.
If none of project.md, team.md, or org.md provides a branching practice, the agent applies hardcoded defaults — trunk-based with squash, base main, target main — and emits PRACTICES_SECTION_EMPTY (advisory-only).
Quick decision matrix
| You want... | Use |
|---|---|
| Default for a new project | Trunk-Based |
| OSS-style PRs with longer-lived branches | GitHub Flow |
| Enterprise release management | GitFlow |
| Versioned releases with stabilisation periods | Release Branches |
| Multiple services in one repo | Monorepo (on top of one of the above) |
If unsure, choose Trunk-Based. It's the lowest-overhead strategy with the strongest CI/CD ecosystem support, and AIDLC's Construction worktrees are designed for it as the default.