This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
# 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.md` for empty-template detection, semantic-topic matching, and the active-space `project.md → team.md → org.md → hardcoded defaults` fallback chain. In the runbooks below, those files live under `aidlc/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:
|
||||
|
||||
1. Read `## Way of Working` from the active space's `project.md`, `team.md`, then `org.md` per `shared/rules-reading.md`; use hardcoded defaults only if all three are empty.
|
||||
2. Resolve flags: `--base main --target main --strategy squash` for the default; deviate only if `team.md` explicitly says otherwise.
|
||||
3. **Create**: invoke `aidlc engine worktree create --slug <bolt-slug> --base main`.
|
||||
4. **Merge** (after Bolt gate approval): caller must be on `main` at the main checkout. Invoke `aidlc engine worktree merge --slug <bolt-slug> --target main --strategy squash --message "<commit message>"`.
|
||||
5. 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 `main` motion (e.g. another Bolt landed first). Tool exits non-zero with `{status: "conflict", conflict_files, detail}`. Orchestrator quotes `detail` to the user.
|
||||
- **Branch already exists.** Pre-audit error; the tool refuses to clobber. Orchestrator should run `discard` first (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:
|
||||
|
||||
1. Read `## Way of Working` from the active space's `project.md`, `team.md`, then `org.md` (including any merge-style statement). The merge-strategy choice (squash vs merge) is what differs from trunk-based.
|
||||
2. Resolve flags: `--base main --target main --strategy <squash|merge>` per affirmation; default to `squash`.
|
||||
3. **Create**: `aidlc engine worktree create --slug <bolt-slug> --base main`.
|
||||
4. **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.
|
||||
5. Return per § Response contract.
|
||||
|
||||
### Failure modes
|
||||
|
||||
- **Same as trunk-based**, plus:
|
||||
- **Stale base for `--strategy merge`.** Long-lived bolt branches against a moving `main` produce 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 `develop` accumulates merge debt against `main` over a release cycle. Painful merges at release-cut time.
|
||||
- Hotfixes require dual-merging (to both `main` and `develop`) — 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:
|
||||
|
||||
1. Read the active space's `## Way of Working`. Look for the integration-branch name (`develop` is the convention; teams sometimes use `integration` or `next`).
|
||||
2. For feature Bolts: `--base <integration> --target <integration> --strategy <merge|squash>`. Default to `merge`.
|
||||
3. 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>` after `aidlc-worktree merge` succeeds. Out of scope for the tool.
|
||||
4. **Create**: `aidlc engine worktree create --slug <bolt-slug> --base <integration>`.
|
||||
5. **Merge**: caller must be on `<integration>` at the main checkout. `aidlc engine worktree merge --slug <bolt-slug> --target <integration> --strategy <merge|squash>`.
|
||||
6. 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 must `cd` to the main checkout and `git 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 in `notes`.
|
||||
|
||||
---
|
||||
|
||||
## 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 `main` so 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:
|
||||
|
||||
1. Read the active space's `## Way of Working`. Look for the release-branch pattern (`release/vX.Y` is the convention).
|
||||
2. Determine which line the Bolt belongs to from the Bolt's metadata (the orchestrator passes a `target_line: main | release/vX.Y` hint). Default to `main` when ambiguous.
|
||||
3. **Create**: `aidlc engine worktree create --slug <bolt-slug> --base <line>`.
|
||||
4. **Merge**: caller on `<line>` at the main checkout. `aidlc engine worktree merge --slug <bolt-slug> --target <line> --strategy merge`.
|
||||
5. If the Bolt was a release-branch fix, include `notes: "consider cherry-pick to main"` in the response — the operator handles the cross-merge.
|
||||
6. 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 `notes` field.
|
||||
- **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:
|
||||
|
||||
1. Resolve the underlying strategy (trunk-based default) per its runbook above.
|
||||
2. The Bolt slug should encode the package scope (e.g. `auth-token-rotation` rather than `feature-1`) so `git worktree list` output stays diagnosable.
|
||||
3. Create + merge identical to the underlying strategy.
|
||||
4. 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 `notes` if 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)
|
||||
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
1. The resolved `## Way of Working` statement from `aidlc/spaces/<active-space>/memory/{project,team,org}.md` (fallback chain in `shared/rules-reading.md`).
|
||||
2. 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.
|
||||
Reference in New Issue
Block a user