@brunosps00/dev-workflow 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -27
- package/bin/dev-workflow.js +1 -1
- package/lib/constants.js +6 -8
- package/lib/init.js +6 -0
- package/lib/install-deps.js +0 -5
- package/lib/migrate-gsd.js +164 -0
- package/lib/uninstall.js +2 -2
- package/package.json +1 -1
- package/scaffold/en/commands/dw-analyze-project.md +6 -11
- package/scaffold/en/commands/dw-autopilot.md +10 -17
- package/scaffold/en/commands/dw-brainstorm.md +2 -2
- package/scaffold/en/commands/dw-bugfix.md +1 -0
- package/scaffold/en/commands/dw-code-review.md +7 -5
- package/scaffold/en/commands/dw-commit.md +6 -0
- package/scaffold/en/commands/dw-create-prd.md +5 -4
- package/scaffold/en/commands/dw-create-techspec.md +7 -4
- package/scaffold/en/commands/dw-deep-research.md +6 -0
- package/scaffold/en/commands/dw-deps-audit.md +1 -0
- package/scaffold/en/commands/dw-find-skills.md +4 -4
- package/scaffold/en/commands/dw-fix-qa.md +1 -0
- package/scaffold/en/commands/dw-generate-pr.md +1 -0
- package/scaffold/en/commands/dw-help.md +10 -27
- package/scaffold/en/commands/dw-intel.md +99 -30
- package/scaffold/en/commands/dw-map-codebase.md +125 -0
- package/scaffold/en/commands/dw-new-project.md +1 -1
- package/scaffold/en/commands/dw-redesign-ui.md +5 -9
- package/scaffold/en/commands/dw-refactoring-analysis.md +8 -6
- package/scaffold/en/commands/dw-review-implementation.md +28 -2
- package/scaffold/en/commands/dw-run-plan.md +14 -20
- package/scaffold/en/commands/dw-run-task.md +5 -4
- package/scaffold/en/commands/dw-update.md +3 -1
- package/scaffold/en/templates/idea-onepager.md +2 -2
- package/scaffold/pt-br/commands/dw-analyze-project.md +6 -11
- package/scaffold/pt-br/commands/dw-autopilot.md +10 -17
- package/scaffold/pt-br/commands/dw-brainstorm.md +2 -2
- package/scaffold/pt-br/commands/dw-bugfix.md +1 -0
- package/scaffold/pt-br/commands/dw-code-review.md +7 -5
- package/scaffold/pt-br/commands/dw-commit.md +6 -0
- package/scaffold/pt-br/commands/dw-create-prd.md +5 -4
- package/scaffold/pt-br/commands/dw-create-techspec.md +7 -4
- package/scaffold/pt-br/commands/dw-deep-research.md +6 -0
- package/scaffold/pt-br/commands/dw-deps-audit.md +1 -0
- package/scaffold/pt-br/commands/dw-find-skills.md +4 -4
- package/scaffold/pt-br/commands/dw-fix-qa.md +1 -0
- package/scaffold/pt-br/commands/dw-generate-pr.md +1 -0
- package/scaffold/pt-br/commands/dw-help.md +10 -27
- package/scaffold/pt-br/commands/dw-intel.md +99 -30
- package/scaffold/pt-br/commands/dw-map-codebase.md +125 -0
- package/scaffold/pt-br/commands/dw-new-project.md +1 -1
- package/scaffold/pt-br/commands/dw-redesign-ui.md +5 -9
- package/scaffold/pt-br/commands/dw-refactoring-analysis.md +8 -6
- package/scaffold/pt-br/commands/dw-review-implementation.md +21 -2
- package/scaffold/pt-br/commands/dw-run-plan.md +16 -22
- package/scaffold/pt-br/commands/dw-run-task.md +5 -4
- package/scaffold/pt-br/commands/dw-update.md +3 -1
- package/scaffold/pt-br/templates/idea-onepager.md +2 -2
- package/scaffold/skills/dw-codebase-intel/SKILL.md +102 -0
- package/scaffold/skills/dw-codebase-intel/agents/intel-updater.md +318 -0
- package/scaffold/skills/dw-codebase-intel/references/api-design-discipline.md +138 -0
- package/scaffold/skills/dw-codebase-intel/references/incremental-update.md +79 -0
- package/scaffold/skills/dw-codebase-intel/references/intel-format.md +208 -0
- package/scaffold/skills/dw-codebase-intel/references/query-patterns.md +148 -0
- package/scaffold/skills/dw-debug-protocol/SKILL.md +106 -0
- package/scaffold/skills/dw-debug-protocol/references/error-categorization.md +127 -0
- package/scaffold/skills/dw-debug-protocol/references/non-reproducible-strategy.md +108 -0
- package/scaffold/skills/dw-debug-protocol/references/six-step-triage.md +139 -0
- package/scaffold/skills/dw-debug-protocol/references/stop-the-line.md +52 -0
- package/scaffold/skills/dw-execute-phase/SKILL.md +133 -0
- package/scaffold/skills/dw-execute-phase/agents/executor.md +264 -0
- package/scaffold/skills/dw-execute-phase/agents/plan-checker.md +215 -0
- package/scaffold/skills/dw-execute-phase/references/atomic-commits.md +143 -0
- package/scaffold/skills/dw-execute-phase/references/plan-verification.md +156 -0
- package/scaffold/skills/dw-execute-phase/references/wave-coordination.md +102 -0
- package/scaffold/skills/dw-git-discipline/SKILL.md +120 -0
- package/scaffold/skills/dw-git-discipline/references/atomic-commits-discipline.md +158 -0
- package/scaffold/skills/dw-git-discipline/references/branch-hygiene.md +150 -0
- package/scaffold/skills/dw-git-discipline/references/trunk-based-pattern.md +82 -0
- package/scaffold/skills/dw-memory/SKILL.md +1 -2
- package/scaffold/skills/dw-simplification/SKILL.md +142 -0
- package/scaffold/skills/dw-simplification/references/behavior-preserving.md +148 -0
- package/scaffold/skills/dw-simplification/references/chestertons-fence.md +152 -0
- package/scaffold/skills/dw-simplification/references/complexity-metrics.md +147 -0
- package/scaffold/skills/dw-source-grounding/SKILL.md +128 -0
- package/scaffold/skills/dw-source-grounding/references/citation-protocol.md +108 -0
- package/scaffold/skills/dw-source-grounding/references/freshness-check.md +108 -0
- package/scaffold/skills/dw-source-grounding/references/source-priority.md +146 -0
- package/scaffold/skills/dw-verify/SKILL.md +0 -1
- package/scaffold/skills/vercel-react-best-practices/SKILL.md +4 -0
- package/scaffold/skills/vercel-react-best-practices/references/perf-discipline.md +122 -0
- package/scaffold/skills/webapp-testing/SKILL.md +5 -0
- package/scaffold/skills/webapp-testing/references/security-boundary.md +115 -0
- package/scaffold/skills/webapp-testing/references/three-workflow-patterns.md +144 -0
- package/scaffold/en/commands/dw-quick.md +0 -85
- package/scaffold/en/commands/dw-resume.md +0 -82
- package/scaffold/pt-br/commands/dw-quick.md +0 -85
- package/scaffold/pt-br/commands/dw-resume.md +0 -82
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Plan verification — the 6-dimension goal-backward analysis
|
|
2
|
+
|
|
3
|
+
The `dw-plan-checker` agent verifies that a `tasks.md` will achieve the PRD goal BEFORE execution starts. This document details each dimension's checks, examples, and pass/fail criteria.
|
|
4
|
+
|
|
5
|
+
## Why pre-execution verification
|
|
6
|
+
|
|
7
|
+
Mid-execution discovery of plan flaws is expensive: context burned, partial commits to revert, deviation logs to resolve. Pre-execution verification catches the same flaws at zero implementation cost. The trade-off: 1-2 minutes of plan-checker time vs. potentially 30+ minutes of mid-execution rework.
|
|
8
|
+
|
|
9
|
+
## The 6 dimensions
|
|
10
|
+
|
|
11
|
+
### 1. Requirement Coverage
|
|
12
|
+
|
|
13
|
+
**Goal:** every PRD requirement (RF-XX) has at least one task addressing it.
|
|
14
|
+
|
|
15
|
+
**Steps:**
|
|
16
|
+
1. Extract numbered requirements from `prd.md`. Pattern: `### RF-NN` or `**RF-NN:**` or numbered list under "Functional Requirements".
|
|
17
|
+
2. Build `Set<RF>` of expected requirements.
|
|
18
|
+
3. Scan `tasks.md` and `<NN>_task.md` files for `Closes RF-XX` / `RF: RF-XX` / `Requirement: RF-XX` markers.
|
|
19
|
+
4. Compute `uncovered = expected - addressed`.
|
|
20
|
+
|
|
21
|
+
**Pass:** `uncovered` is empty.
|
|
22
|
+
**Revise:** uncovered non-empty AND fixable by adding tasks.
|
|
23
|
+
**Block:** PRD has no numbered requirements (`expected` is empty) — verifying intent is impossible.
|
|
24
|
+
|
|
25
|
+
**Common failures:**
|
|
26
|
+
- Planner forgot a non-obvious requirement (e.g., audit logging) buried in PRD prose
|
|
27
|
+
- Compound requirement ("user can sign up AND verify email") only addressed for one half
|
|
28
|
+
- Requirement covered indirectly but no task explicitly marks the closure
|
|
29
|
+
|
|
30
|
+
### 2. Task Completeness
|
|
31
|
+
|
|
32
|
+
**Goal:** each task has all four parts: files, action, verification, done criteria.
|
|
33
|
+
|
|
34
|
+
**Steps:**
|
|
35
|
+
For each task in `tasks.md`:
|
|
36
|
+
- Open `<NN>_task.md` (or inline section)
|
|
37
|
+
- Check for required sections:
|
|
38
|
+
- `## Files` (or `Files to create/modify:`) — explicit paths
|
|
39
|
+
- `## Action` (or `Implement:`) — what to do, concrete
|
|
40
|
+
- `## Verification` (or `Verify:`) — how to know it worked (linter, tests, build, manual)
|
|
41
|
+
- `## Done` (or `Done when:`) — criteria for marking `[x]`
|
|
42
|
+
|
|
43
|
+
**Pass:** all four sections present in every task.
|
|
44
|
+
**Revise:** ≥1 task missing one section.
|
|
45
|
+
|
|
46
|
+
**Common failures:**
|
|
47
|
+
- "Files: TBD" or "Files: see techspec" — too vague
|
|
48
|
+
- No verification → executor can't decide when to commit
|
|
49
|
+
- Done criteria absent → executor commits but can't mark `[x]` confidently
|
|
50
|
+
|
|
51
|
+
### 3. Dependency Soundness
|
|
52
|
+
|
|
53
|
+
**Goal:** `Depends on:` graph is acyclic, references valid, waves are reasonable.
|
|
54
|
+
|
|
55
|
+
**Steps:**
|
|
56
|
+
1. Parse every task's `Depends on:` field (none / comma-separated task numbers).
|
|
57
|
+
2. Build directed graph (node = task, edge = depends-on relation).
|
|
58
|
+
3. Topological sort:
|
|
59
|
+
- Cycle → BLOCK with the cycle path
|
|
60
|
+
- Reference to non-existent task number → REVISE with the dangling ref
|
|
61
|
+
4. Compute wave widths. Any wave > 8 tasks → REVISE (split with synthetic barrier).
|
|
62
|
+
|
|
63
|
+
**Pass:** topological sort succeeds, all refs valid, waves ≤ 8 wide.
|
|
64
|
+
|
|
65
|
+
**Common failures:**
|
|
66
|
+
- Bidirectional dependency (`02 depends on 03; 03 depends on 02`) — likely planner confusion; needs re-think
|
|
67
|
+
- Typo in `Depends on: 02` when the task is actually numbered `2.5` or `03`
|
|
68
|
+
- 10 independent test files all in wave 1 → split into smaller waves
|
|
69
|
+
|
|
70
|
+
### 4. Artifact Wiring
|
|
71
|
+
|
|
72
|
+
**Goal:** every artifact produced by a task is consumed by a downstream task OR is a leaf deliverable referenced in PRD's user stories.
|
|
73
|
+
|
|
74
|
+
**Steps:**
|
|
75
|
+
1. For each task, identify what it PRODUCES:
|
|
76
|
+
- New files (from `Files: + src/foo.ts`)
|
|
77
|
+
- New exports (from `Implement: export function bar()`)
|
|
78
|
+
- New endpoints (from `Action: add POST /api/baz`)
|
|
79
|
+
2. For each downstream task (later in topo order), identify what it CONSUMES:
|
|
80
|
+
- Imports (from `Files: src/quux.ts (modify) — imports bar`)
|
|
81
|
+
- References (from `Action: call /api/baz`)
|
|
82
|
+
3. Cross-reference: every produced artifact should be either consumed downstream OR explicitly mentioned in PRD as a user-facing deliverable.
|
|
83
|
+
|
|
84
|
+
**Pass:** zero orphan artifacts.
|
|
85
|
+
**Revise:** ≥1 artifact created without consumer or PRD reference — likely incomplete plan.
|
|
86
|
+
|
|
87
|
+
**Common failures:**
|
|
88
|
+
- Task creates a service but no task wires it into the router
|
|
89
|
+
- Task creates a migration file but no task runs it (`prisma migrate deploy`)
|
|
90
|
+
- Task creates a config but no task reads it
|
|
91
|
+
|
|
92
|
+
### 5. Context Budget
|
|
93
|
+
|
|
94
|
+
**Goal:** the phase fits in a practical execution window.
|
|
95
|
+
|
|
96
|
+
**Steps:**
|
|
97
|
+
1. Count tasks. Practical limit: 12 tasks per phase before quality degrades.
|
|
98
|
+
2. Estimate aggregate file changes: sum of files mentioned in `Files:` across all tasks. Limit: 30 files per phase.
|
|
99
|
+
3. Check parallelism setup: are wave-1 tasks running in parallel? (frontmatter `parallel: true` or default).
|
|
100
|
+
|
|
101
|
+
**Pass:** ≤12 tasks, ≤30 aggregate files, wave 1 has parallel execution if multi-task.
|
|
102
|
+
|
|
103
|
+
**Revise:** > 12 tasks → suggest splitting into 2 phases. > 30 files → reduce scope or split.
|
|
104
|
+
|
|
105
|
+
**Common failures:**
|
|
106
|
+
- Mega-phase with 20 tasks because the planner couldn't decompose
|
|
107
|
+
- Single-task waves where parallelism was forgotten
|
|
108
|
+
|
|
109
|
+
### 6. Constraint Compliance
|
|
110
|
+
|
|
111
|
+
**Goal:** tasks honor locked decisions and project conventions.
|
|
112
|
+
|
|
113
|
+
**Steps:**
|
|
114
|
+
1. Read `.dw/rules/index.md` and `.dw/rules/<module>.md` for relevant modules.
|
|
115
|
+
2. Read `CONTEXT.md` if exists; extract `## Decisions` (LOCKED) section.
|
|
116
|
+
3. Read `./CLAUDE.md` for project hard constraints.
|
|
117
|
+
4. For each task, check if its `Action` or `Files` violate:
|
|
118
|
+
- A locked decision
|
|
119
|
+
- A project rule (forbidden pattern, mandated tool)
|
|
120
|
+
- A CLAUDE.md directive
|
|
121
|
+
|
|
122
|
+
**Pass:** zero violations.
|
|
123
|
+
**Block:** ≥1 hard violation. The plan must change before execution.
|
|
124
|
+
|
|
125
|
+
**Common failures:**
|
|
126
|
+
- Locked decision says "framework: Fastify" but task says "use Express"
|
|
127
|
+
- Project rules forbid raw SQL; task uses `pg.query()` directly
|
|
128
|
+
- CLAUDE.md says "no env files committed"; task adds `.env.production`
|
|
129
|
+
|
|
130
|
+
## Verdict resolution
|
|
131
|
+
|
|
132
|
+
After running all 6 dimensions:
|
|
133
|
+
|
|
134
|
+
| Outcome | Verdict |
|
|
135
|
+
|---------|---------|
|
|
136
|
+
| All 6 PASS | PASS — proceed to execution |
|
|
137
|
+
| Any REVISE, no BLOCK | REVISE — re-plan |
|
|
138
|
+
| Any BLOCK | BLOCK — surface to user, no auto-replan |
|
|
139
|
+
|
|
140
|
+
`PASS` is the only state that allows `/dw-execute-phase` to proceed.
|
|
141
|
+
|
|
142
|
+
## Bounded revision loop
|
|
143
|
+
|
|
144
|
+
The plan-checker is part of a bounded quality loop:
|
|
145
|
+
|
|
146
|
+
1. `/dw-create-tasks` produces v1 of `tasks.md`
|
|
147
|
+
2. `/dw-plan-checker` runs → REVISE
|
|
148
|
+
3. `/dw-create-tasks --revise` produces v2 (consumes plan-checker's issues as input)
|
|
149
|
+
4. `/dw-plan-checker` runs → PASS or REVISE again
|
|
150
|
+
5. After 3 revisions without reaching PASS → escalate to user (something fundamental is wrong)
|
|
151
|
+
|
|
152
|
+
The escalation cap prevents infinite loops where the planner can't satisfy the verifier. At 3 strikes, the user sees the diff between PRD and the failing tasks.md and decides next step.
|
|
153
|
+
|
|
154
|
+
## Time budget
|
|
155
|
+
|
|
156
|
+
Plan-checker target: 1-2 minutes. The 6 dimensions are mostly file reads and grep operations; no heavy analysis. If plan-checker exceeds 5 minutes, it's a sign the agent is over-thinking — bias toward issuing REVISE for ambiguous cases rather than analyzing them deeply.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Wave coordination — how the executor parallelizes tasks
|
|
2
|
+
|
|
3
|
+
Tasks within a phase have dependencies. Some can run in parallel; others must wait. The executor groups tasks into **waves** and runs each wave as parallel subagent dispatches, with sequential ordering between waves.
|
|
4
|
+
|
|
5
|
+
## Wave computation
|
|
6
|
+
|
|
7
|
+
Input: `tasks.md` with each task carrying a `Depends on:` field (none, or comma-separated task numbers).
|
|
8
|
+
|
|
9
|
+
Algorithm: topological sort.
|
|
10
|
+
|
|
11
|
+
1. Build dependency graph: node per task, edge from each `Depends on:` to the dependent task.
|
|
12
|
+
2. Cycle check: if a cycle exists → abort with `EXEC-FAILED: dependency cycle`.
|
|
13
|
+
3. Wave assignment:
|
|
14
|
+
- Wave 1 = tasks with zero dependencies
|
|
15
|
+
- Wave N = tasks whose all dependencies are in waves 1..N-1
|
|
16
|
+
4. Output: ordered list of waves, each wave a list of task numbers.
|
|
17
|
+
|
|
18
|
+
## Example
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
tasks.md:
|
|
22
|
+
- 01 Create user schema Depends on: none
|
|
23
|
+
- 02 Create user model Depends on: 01
|
|
24
|
+
- 03 Create order schema Depends on: none
|
|
25
|
+
- 04 Wire auth middleware Depends on: 02
|
|
26
|
+
- 05 Add login endpoint Depends on: 04
|
|
27
|
+
- 06 Add order endpoint Depends on: 02, 03
|
|
28
|
+
- 07 Wire validation rules Depends on: 04, 06
|
|
29
|
+
|
|
30
|
+
Computed waves:
|
|
31
|
+
Wave 1: [01, 03] (no deps; can run in parallel)
|
|
32
|
+
Wave 2: [02] (depends on 01)
|
|
33
|
+
Wave 3: [04, 06] (depend on wave 2)
|
|
34
|
+
Wave 4: [05, 07] (depend on wave 3)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Parallel execution within a wave
|
|
38
|
+
|
|
39
|
+
Within a wave, the executor dispatches tasks in parallel via subagent calls (one subagent per task). Each subagent:
|
|
40
|
+
|
|
41
|
+
1. Reads its `<NN>_task.md`
|
|
42
|
+
2. Implements
|
|
43
|
+
3. Verifies (lint/tests/build)
|
|
44
|
+
4. Commits atomically (per `atomic-commits.md`)
|
|
45
|
+
5. Marks `[x]` in `tasks.md`
|
|
46
|
+
6. Returns success or deviation status to the orchestrating executor
|
|
47
|
+
|
|
48
|
+
The orchestrating executor:
|
|
49
|
+
|
|
50
|
+
- Spawns N subagents in parallel (single message, N tool calls)
|
|
51
|
+
- Waits for all to return
|
|
52
|
+
- If all PASS → proceed to next wave
|
|
53
|
+
- If ANY return deviation/blocked → resolve before next wave (see deviation rules in `agents/executor.md`)
|
|
54
|
+
|
|
55
|
+
## Wave width limits
|
|
56
|
+
|
|
57
|
+
Practical caps to keep context budget sane:
|
|
58
|
+
|
|
59
|
+
- **Soft cap: 5 tasks per wave** — tested as the upper bound where parallel execution stays efficient
|
|
60
|
+
- **Hard cap: 8 tasks per wave** — beyond this, the orchestrator's context fills with subagent results faster than tasks complete
|
|
61
|
+
|
|
62
|
+
If a wave exceeds the hard cap, the planner should split: introduce a synthetic dependency to bisect the wave. Example: if Wave 3 has 10 independent tasks, force tasks 06-10 to depend on a "wave 3a barrier" so they go to Wave 3.5.
|
|
63
|
+
|
|
64
|
+
The plan-checker (Dimension 5) flags wide waves before execution.
|
|
65
|
+
|
|
66
|
+
## Cross-wave atomicity
|
|
67
|
+
|
|
68
|
+
Each wave is a checkpoint:
|
|
69
|
+
|
|
70
|
+
- After all tasks in a wave commit, run a `git status` check — should be clean (everything committed).
|
|
71
|
+
- If any task in a wave failed permanently (Rule 3 deviation), abort BEFORE starting the next wave. Leave the partial work committed; surface to user via `EXEC-BLOCKED`.
|
|
72
|
+
- Don't run waves N+1 with broken commits in wave N. The dependencies aren't satisfied.
|
|
73
|
+
|
|
74
|
+
## Order within a wave
|
|
75
|
+
|
|
76
|
+
Within a wave, order doesn't matter logically (no deps between same-wave tasks). But for **commit history readability**, the executor should commit in numeric order of task number (01 commit before 02 even if both are wave 1). The parallel subagent results may arrive out of order; the executor collects and commits sequentially.
|
|
77
|
+
|
|
78
|
+
This means: subagents return their changes (files written, but NOT committed). The executor commits them in numeric order.
|
|
79
|
+
|
|
80
|
+
(Alternative: subagents commit independently and accept commit interleaving. Pick this if commit-order doesn't matter for the project; the orchestrator sets a flag.)
|
|
81
|
+
|
|
82
|
+
## When to NOT use waves
|
|
83
|
+
|
|
84
|
+
Single-task changes (`/dw-quick`, `/dw-run-task`) bypass waves entirely. Waves are for `/dw-run-plan` and `/dw-execute-phase` — phase-scale execution.
|
|
85
|
+
|
|
86
|
+
## Verification of wave structure (pre-execution)
|
|
87
|
+
|
|
88
|
+
The plan-checker (Dimension 3 in `plan-checker.md`) verifies:
|
|
89
|
+
- Topological sort succeeds (no cycles)
|
|
90
|
+
- All `Depends on:` references point to existing tasks
|
|
91
|
+
- No wave exceeds the hard cap (8 tasks)
|
|
92
|
+
|
|
93
|
+
If these fail, plan-checker returns REVISE before any code is touched.
|
|
94
|
+
|
|
95
|
+
## Resume after checkpoint
|
|
96
|
+
|
|
97
|
+
If the executor checkpoints mid-phase, `active-session.md` records:
|
|
98
|
+
- `last_completed_task`
|
|
99
|
+
- `last_wave_completed`
|
|
100
|
+
- `remaining_tasks`
|
|
101
|
+
|
|
102
|
+
`/dw-resume` reads this, recomputes waves from `tasks.md`, skips already-committed tasks, and resumes from the wave containing `last_completed_task + 1`.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dw-git-discipline
|
|
3
|
+
description: Use when committing or opening a PR — applies trunk-based development, atomic commit discipline (one intent per commit, refactor separate from feature), conventional commit messages, and branch hygiene so history is bisectable and reviewable.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Git Discipline
|
|
7
|
+
|
|
8
|
+
> **Inspired by** [`addyosmani/agent-skills/git-workflow-and-versioning`](https://github.com/addyosmani/agent-skills/tree/main/git-workflow-and-versioning) (MIT). Trunk-based pattern, atomic commit principles, and branch-hygiene patterns adapted from Addy Osmani's work; specifics rewritten for dev-workflow's commit and PR commands.
|
|
9
|
+
|
|
10
|
+
History is documentation. Bad commit hygiene corrupts the documentation; good hygiene makes future debugging cheap. This skill encodes the rules.
|
|
11
|
+
|
|
12
|
+
## Three core principles
|
|
13
|
+
|
|
14
|
+
### 1. One intent per commit
|
|
15
|
+
|
|
16
|
+
A commit answers ONE question: "what did I change and why?" If your commit message has the word "and" connecting two unrelated changes — split it.
|
|
17
|
+
|
|
18
|
+
Common splits:
|
|
19
|
+
- Refactor + feature → two commits (refactor first, feature second).
|
|
20
|
+
- Fix + style cleanup → two commits.
|
|
21
|
+
- Type fix + behavior change → two commits.
|
|
22
|
+
- Multiple bug fixes → one commit per bug.
|
|
23
|
+
|
|
24
|
+
**Why this matters:** When something breaks 6 weeks later, `git bisect` returns the commit that introduced it. If that commit also did a rename, a refactor, and a feature, you've gained nothing. If it did one thing, you know what to revert.
|
|
25
|
+
|
|
26
|
+
See `references/atomic-commits-discipline.md` for the discipline in detail.
|
|
27
|
+
|
|
28
|
+
### 2. Trunk-based, short-lived branches
|
|
29
|
+
|
|
30
|
+
Long-lived branches diverge from `main` and become merge nightmares. The discipline:
|
|
31
|
+
|
|
32
|
+
- Branches live 1-3 days, max a week.
|
|
33
|
+
- Daily merge or rebase from `main` to keep close to trunk.
|
|
34
|
+
- Incomplete work behind feature flags, not behind a multi-week branch.
|
|
35
|
+
- Small PRs (under ~400 lines diff). If bigger, ask: can this be split into independently-mergeable pieces?
|
|
36
|
+
|
|
37
|
+
See `references/trunk-based-pattern.md` for when this bends and when it doesn't.
|
|
38
|
+
|
|
39
|
+
### 3. Branch + commit message hygiene
|
|
40
|
+
|
|
41
|
+
- **Branch names:** `feat/<scope>`, `fix/<scope>`, `refactor/<scope>`, `chore/<scope>`. Lowercase, kebab-case, ≤40 chars.
|
|
42
|
+
- **Commit messages:** Conventional Commits (`type(scope): subject`) — `feat`, `fix`, `refactor`, `docs`, `chore`, `test`, `style`, `build`, `ci`, `perf`.
|
|
43
|
+
- **Subject line:** ≤72 chars, imperative mood ("add" not "added"), no trailing period.
|
|
44
|
+
- **Body (when needed):** explain WHY, not WHAT. The diff already shows what.
|
|
45
|
+
- **Footer:** breaking-change marker, issue references, co-author lines.
|
|
46
|
+
|
|
47
|
+
See `references/branch-hygiene.md` for naming conventions and rebase-vs-merge guidance.
|
|
48
|
+
|
|
49
|
+
## What this skill enforces
|
|
50
|
+
|
|
51
|
+
When wired into `/dw-commit`, every commit must:
|
|
52
|
+
|
|
53
|
+
1. Have a single logical intent (one feature, one fix, one refactor — not mixed).
|
|
54
|
+
2. Pass lint + tests + build BEFORE the commit is created.
|
|
55
|
+
3. Use Conventional Commits format with correct type/scope.
|
|
56
|
+
4. Have a body that explains WHY for non-trivial changes.
|
|
57
|
+
5. NOT skip pre-commit hooks (`--no-verify` is forbidden unless user explicitly authorizes).
|
|
58
|
+
6. NOT amend an already-pushed commit (history rewrites on shared branches break collaborators).
|
|
59
|
+
|
|
60
|
+
When wired into `/dw-generate-pr`, every PR must:
|
|
61
|
+
|
|
62
|
+
1. Have an explanatory description with summary + test plan, not just commit-list dump.
|
|
63
|
+
2. Stay reasonably scoped (large PRs flagged with split suggestion).
|
|
64
|
+
3. Have a branch that follows naming conventions.
|
|
65
|
+
4. Reference an issue / PRD / spec when applicable.
|
|
66
|
+
|
|
67
|
+
## Quick reference: when to do what
|
|
68
|
+
|
|
69
|
+
| Situation | Action |
|
|
70
|
+
|-----------|--------|
|
|
71
|
+
| Mixed refactor + feature in working dir | Stage refactor → commit → stage feature → commit |
|
|
72
|
+
| Pre-commit hook fails | Investigate. Fix root cause. NEVER `--no-verify` |
|
|
73
|
+
| Want to "tidy up" before PR | `git rebase -i` BEFORE pushing — never after |
|
|
74
|
+
| Already pushed and need to fix message | `git revert` + new commit. Don't force-push to shared. |
|
|
75
|
+
| Branch is 5 days old, tons of conflicts | Rebase from main daily; don't let drift compound |
|
|
76
|
+
| PR has 2,000 lines | Split. Identify natural seams: schema, backend, frontend, tests |
|
|
77
|
+
| Stuck mid-merge with conflicts | Resolve, don't `git checkout --theirs` blindly. The conflict is information |
|
|
78
|
+
|
|
79
|
+
## What this skill does NOT do
|
|
80
|
+
|
|
81
|
+
- It does not push, force-push, or create branches without explicit user request.
|
|
82
|
+
- It does not amend commits without explicit user request.
|
|
83
|
+
- It does not collapse multiple commits via `git rebase -i` on already-pushed branches.
|
|
84
|
+
- It does not enforce a specific commit count per PR — only that each commit is atomic.
|
|
85
|
+
|
|
86
|
+
## Integration with dev-workflow commands
|
|
87
|
+
|
|
88
|
+
- `/dw-commit` runs this skill — verifies lint/tests/build green, drafts a Conventional Commits message, splits commits if multi-intent detected.
|
|
89
|
+
- `/dw-generate-pr` uses this skill to validate branch naming, PR body structure, and scope.
|
|
90
|
+
- `/dw-run-task` and `/dw-run-plan` follow the atomic-commit discipline when their executor commits work — one task = one commit (or one logical sub-task).
|
|
91
|
+
|
|
92
|
+
## Anti-patterns this skill prevents
|
|
93
|
+
|
|
94
|
+
- "WIP" commits getting merged to `main` (pre-PR cleanup expected).
|
|
95
|
+
- "Fix typo" follow-up commits that should have been amended in feature branch.
|
|
96
|
+
- Commits with hooks bypassed (`--no-verify`) — root cause should be fixed instead.
|
|
97
|
+
- Long-lived branches that drift from main.
|
|
98
|
+
- PR descriptions that are just `git log` dumps.
|
|
99
|
+
- Force-pushes to shared branches.
|
|
100
|
+
- Commits that mix unrelated changes.
|
|
101
|
+
|
|
102
|
+
## When discipline bends
|
|
103
|
+
|
|
104
|
+
Real-world software can't always be perfect:
|
|
105
|
+
|
|
106
|
+
- **Hotfix to production:** atomic still applies; hygiene bends only on subject-line conciseness if needed for clarity.
|
|
107
|
+
- **Massive auto-generated change** (lockfile, codemod): one commit is fine; mark `chore(deps)` or `refactor(codemod)` clearly.
|
|
108
|
+
- **Reverts:** use `git revert <sha>` (preserves history) over `git reset --hard` (rewrites history). Both create one commit; only revert is safe on shared branches.
|
|
109
|
+
|
|
110
|
+
## Verification before committing
|
|
111
|
+
|
|
112
|
+
- [ ] Lint passes
|
|
113
|
+
- [ ] Tests pass
|
|
114
|
+
- [ ] Build passes
|
|
115
|
+
- [ ] One logical intent
|
|
116
|
+
- [ ] Conventional Commits subject
|
|
117
|
+
- [ ] Body explains WHY (when non-trivial)
|
|
118
|
+
- [ ] No `--no-verify`
|
|
119
|
+
- [ ] No amend of pushed commit
|
|
120
|
+
- [ ] Branch name follows convention
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Atomic commits — one intent per commit
|
|
2
|
+
|
|
3
|
+
A commit is atomic if it represents exactly one logical change. Atomic commits are the foundation of `git bisect`, `git revert`, code review quality, and reading history six months later.
|
|
4
|
+
|
|
5
|
+
## The single-intent rule
|
|
6
|
+
|
|
7
|
+
If your commit message naturally contains "and" connecting two things — that's two commits.
|
|
8
|
+
|
|
9
|
+
| Bad subject | Better |
|
|
10
|
+
|-------------|--------|
|
|
11
|
+
| `feat: add login form and fix navbar bug` | Two commits: `feat(login): add form`, `fix(nav): correct mobile menu` |
|
|
12
|
+
| `refactor: extract user service and add caching` | Two commits: `refactor(user): extract service`, `feat(user): add result cache` |
|
|
13
|
+
| `chore: update deps and reformat` | Two commits: `chore(deps): bump foo to 2.0`, `style: apply prettier sweep` |
|
|
14
|
+
| `fix: handle null and add tests` | Two commits: `test(payment): cover null amount case`, `fix(payment): handle null amount` (or merge tests + fix when they're the same logical change — see below) |
|
|
15
|
+
|
|
16
|
+
**Exception:** if you fix a bug AND add the regression test for it, those belong in ONE commit. The test proves the bug and proves the fix; separating them loses the link.
|
|
17
|
+
|
|
18
|
+
## Refactor vs feature: always separate
|
|
19
|
+
|
|
20
|
+
The most common atomicity violation: refactoring "while I'm in here" alongside a feature.
|
|
21
|
+
|
|
22
|
+
Bad:
|
|
23
|
+
```
|
|
24
|
+
feat(orders): add bulk-order export AND extract OrderRepository
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Good:
|
|
28
|
+
```
|
|
29
|
+
refactor(orders): extract OrderRepository (no behavior change)
|
|
30
|
+
feat(orders): add bulk-order export endpoint
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Why: when bulk-order export turns out to have a bug 2 months later, `git bisect` lands on the second commit. The first commit is a clean prior baseline you can compare against. If they were combined, the refactor's surface area dilutes the diagnosis.
|
|
34
|
+
|
|
35
|
+
## Practical: how to make commits atomic
|
|
36
|
+
|
|
37
|
+
When you find yourself with mixed changes in the working directory:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# 1. See what's changed
|
|
41
|
+
git status
|
|
42
|
+
git diff
|
|
43
|
+
|
|
44
|
+
# 2. Stage just the refactor (no behavior change)
|
|
45
|
+
git add -p # interactive — pick hunks one by one
|
|
46
|
+
# Or stage specific files:
|
|
47
|
+
git add src/orders/repository.ts
|
|
48
|
+
|
|
49
|
+
# 3. Verify what's staged matches one logical intent
|
|
50
|
+
git diff --cached
|
|
51
|
+
|
|
52
|
+
# 4. Commit
|
|
53
|
+
git commit -m "refactor(orders): extract OrderRepository (no behavior change)"
|
|
54
|
+
|
|
55
|
+
# 5. Stage the feature
|
|
56
|
+
git add src/orders/bulk-export.ts src/orders/__tests__/bulk-export.test.ts
|
|
57
|
+
|
|
58
|
+
# 6. Commit
|
|
59
|
+
git commit -m "feat(orders): add bulk-order export endpoint"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`git add -p` is the workhorse for unmixing changes. Practice it.
|
|
63
|
+
|
|
64
|
+
## Commit message structure
|
|
65
|
+
|
|
66
|
+
Conventional Commits format:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
type(scope): subject
|
|
70
|
+
|
|
71
|
+
body — explains WHY, not what (the diff already shows what)
|
|
72
|
+
|
|
73
|
+
Footer: BREAKING CHANGE, Refs, Co-Authored-By
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Type vocabulary:**
|
|
77
|
+
|
|
78
|
+
| Type | Use for |
|
|
79
|
+
|------|---------|
|
|
80
|
+
| `feat` | New user-facing capability |
|
|
81
|
+
| `fix` | Bug fix |
|
|
82
|
+
| `refactor` | Code change with NO behavior change |
|
|
83
|
+
| `perf` | Behavior unchanged but faster |
|
|
84
|
+
| `docs` | Docs-only |
|
|
85
|
+
| `test` | Test additions/changes only |
|
|
86
|
+
| `style` | Formatting, no semantic change |
|
|
87
|
+
| `chore` | Build, deps, config, no source change |
|
|
88
|
+
| `ci` | CI pipeline changes only |
|
|
89
|
+
| `build` | Build system changes |
|
|
90
|
+
|
|
91
|
+
**Subject line rules:**
|
|
92
|
+
- ≤72 chars
|
|
93
|
+
- Imperative ("add" not "added", "fix" not "fixes")
|
|
94
|
+
- No trailing period
|
|
95
|
+
- Lowercase after the colon
|
|
96
|
+
|
|
97
|
+
**Body rules:**
|
|
98
|
+
- Wrap at 72-100 chars per line
|
|
99
|
+
- Blank line between subject and body
|
|
100
|
+
- WHY > WHAT
|
|
101
|
+
- Reference issues, PRDs, ADRs when relevant
|
|
102
|
+
|
|
103
|
+
## When to write a body
|
|
104
|
+
|
|
105
|
+
You don't need a body for trivial changes. You need one when:
|
|
106
|
+
|
|
107
|
+
- The reason for the change isn't obvious from the diff (e.g., a workaround for a bug in a dependency).
|
|
108
|
+
- The change addresses an issue with non-obvious consequences (e.g., performance, security).
|
|
109
|
+
- The change is a deliberate design choice over alternatives (briefly note the rejected option).
|
|
110
|
+
- The change has a non-obvious blast radius.
|
|
111
|
+
|
|
112
|
+
A good rule: if a future maintainer would say "huh, why?" reading the diff, write a body.
|
|
113
|
+
|
|
114
|
+
## What goes in the footer
|
|
115
|
+
|
|
116
|
+
- `BREAKING CHANGE: <explanation>` — for breaking-change commits.
|
|
117
|
+
- `Refs #123` — issue reference.
|
|
118
|
+
- `Co-Authored-By: Name <email>` — for pair work or AI assistance.
|
|
119
|
+
|
|
120
|
+
## Bisect-friendly commits
|
|
121
|
+
|
|
122
|
+
For `git bisect` to work, every commit must:
|
|
123
|
+
- Build
|
|
124
|
+
- Pass tests
|
|
125
|
+
- Be a coherent state (not "WIP, half-implemented")
|
|
126
|
+
|
|
127
|
+
If you can't bisect through your branch, the commits aren't atomic enough. Squash before merging is acceptable; intra-branch WIP commits get rebased away before push.
|
|
128
|
+
|
|
129
|
+
## What to do with WIP / "save point" commits
|
|
130
|
+
|
|
131
|
+
Local WIP is fine. Pushed WIP is not.
|
|
132
|
+
|
|
133
|
+
Workflow:
|
|
134
|
+
1. Make many small commits as you work (saves your progress).
|
|
135
|
+
2. Before pushing OR before opening PR, `git rebase -i origin/main` to reorganize:
|
|
136
|
+
- Squash WIP commits into their logical parent.
|
|
137
|
+
- Drop accidental commits.
|
|
138
|
+
- Reorder so refactor comes before feature.
|
|
139
|
+
- Edit messages.
|
|
140
|
+
3. Push the cleaned history.
|
|
141
|
+
|
|
142
|
+
After pushing, no rewrites unless explicitly authorized — others may have pulled.
|
|
143
|
+
|
|
144
|
+
## Common atomic-commit mistakes
|
|
145
|
+
|
|
146
|
+
- **"Tiny extra fix" piggy-backed.** "While I was in there" → separate commit.
|
|
147
|
+
- **Reformat sweep mixed with logic change.** Apply formatter as its own commit BEFORE making logic changes.
|
|
148
|
+
- **Test fixture changes mixed with code changes.** If the fixture change is just to support the new code, fine — keep together. If the fixture was wrong before and you're fixing it, separate commit.
|
|
149
|
+
- **Lockfile churn separate from intent.** Sometimes lockfile updates accompany intentional dep bumps; that's fine. But noisy lockfile changes from running `npm install` for unrelated reasons → revert.
|
|
150
|
+
- **Generated file diffs mixed in.** Generated files (build artifacts, generated types) should regenerate from source automatically; don't commit them by hand alongside source changes if you can avoid it.
|
|
151
|
+
|
|
152
|
+
## What atomic commits unlock
|
|
153
|
+
|
|
154
|
+
- `git bisect` finds the exact commit that broke X — and that commit is small enough to inspect quickly.
|
|
155
|
+
- `git revert <sha>` removes ONE feature without unwinding others.
|
|
156
|
+
- Code review focuses: each commit reviewed in isolation makes a tight discussion possible.
|
|
157
|
+
- Cherry-picking to other branches works cleanly when the change is one thing.
|
|
158
|
+
- Commit messages become reliable changelog material.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Branch hygiene — naming, lifetime, rebase vs merge
|
|
2
|
+
|
|
3
|
+
Branches are conversations between developers. Names, lifetimes, and integration choices shape that conversation.
|
|
4
|
+
|
|
5
|
+
## Branch naming conventions
|
|
6
|
+
|
|
7
|
+
Format: `<type>/<scope>` or `<type>/<scope>-<short-description>`
|
|
8
|
+
|
|
9
|
+
| Prefix | When |
|
|
10
|
+
|--------|------|
|
|
11
|
+
| `feat/` | New user-facing capability |
|
|
12
|
+
| `fix/` | Bug fix |
|
|
13
|
+
| `refactor/` | No behavior change, internal restructure |
|
|
14
|
+
| `perf/` | Performance improvement |
|
|
15
|
+
| `chore/` | Build, deps, config |
|
|
16
|
+
| `docs/` | Documentation only |
|
|
17
|
+
| `test/` | Test additions or fixes |
|
|
18
|
+
| `experiment/` | Spike / exploration; usually NOT merged |
|
|
19
|
+
| `hotfix/` | Urgent production fix |
|
|
20
|
+
| `release/` | Release preparation (if your team uses release branches) |
|
|
21
|
+
|
|
22
|
+
Rules:
|
|
23
|
+
- Lowercase, kebab-case.
|
|
24
|
+
- ≤40 characters total.
|
|
25
|
+
- Match the prefix to the dominant change type if mixed.
|
|
26
|
+
- Include a PRD/issue reference when one exists: `feat/prd-user-onboarding`, `fix/issue-1234-login-loop`.
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
|
|
30
|
+
| Good | Bad |
|
|
31
|
+
|------|-----|
|
|
32
|
+
| `feat/user-onboarding` | `feature/UserOnboarding` (capitalized) |
|
|
33
|
+
| `fix/login-redirect-loop` | `bug-fix-stuff` (no prefix, vague) |
|
|
34
|
+
| `refactor/extract-user-service` | `mybranch` (meaningless) |
|
|
35
|
+
| `chore/bump-react-19` | `update-deps-and-tests` (multi-intent in name) |
|
|
36
|
+
|
|
37
|
+
## Branch lifetime
|
|
38
|
+
|
|
39
|
+
| Branch type | Target lifetime |
|
|
40
|
+
|-------------|-----------------|
|
|
41
|
+
| `feat/` for normal feature | 1-3 days |
|
|
42
|
+
| `fix/` for normal bug | hours to 1 day |
|
|
43
|
+
| `hotfix/` | hours, ship same day |
|
|
44
|
+
| `refactor/` for small refactor | hours to 1 day |
|
|
45
|
+
| `refactor/` for major refactor | break into multiple `refactor/`+ branches, each 1-3 days |
|
|
46
|
+
| `experiment/` | as long as needed, but NOT merged to main without conversion |
|
|
47
|
+
| `release/` | until release ships |
|
|
48
|
+
|
|
49
|
+
A branch older than a week is a smell. Either:
|
|
50
|
+
- It's blocked on review (fix the review process).
|
|
51
|
+
- It's too big (split it).
|
|
52
|
+
- It should have been a feature flag on `main` (kill the branch, do it on main).
|
|
53
|
+
|
|
54
|
+
## Daily integration with `main`
|
|
55
|
+
|
|
56
|
+
While on a branch, integrate from `main` daily:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git fetch origin
|
|
60
|
+
git rebase origin/main # preferred for personal branches
|
|
61
|
+
# OR
|
|
62
|
+
git merge origin/main # acceptable for shared branches
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Why daily:
|
|
66
|
+
- 1 day of drift = trivial conflicts.
|
|
67
|
+
- 7 days of drift = cascading conflicts; same files changed on both sides multiple times.
|
|
68
|
+
- Conflicts caught daily are individually small; conflicts caught at PR time are an avalanche.
|
|
69
|
+
|
|
70
|
+
## Rebase vs merge
|
|
71
|
+
|
|
72
|
+
| Situation | Choice |
|
|
73
|
+
|-----------|--------|
|
|
74
|
+
| Personal branch, never shared | Rebase. Linear history. |
|
|
75
|
+
| Shared branch (others have pulled) | Merge. Rebase rewrites history; collaborators' branches diverge. |
|
|
76
|
+
| Pulling latest `main` into your active feature branch | Rebase, IF nobody else has pulled your branch yet. |
|
|
77
|
+
| Merging your feature into `main` via PR | Whatever the team standard is. Squash-merge is fine if commits weren't atomic; preserve commits if they were. |
|
|
78
|
+
|
|
79
|
+
Rule of thumb: **rebase what's yours; merge what's shared.**
|
|
80
|
+
|
|
81
|
+
## When NOT to force-push
|
|
82
|
+
|
|
83
|
+
After a `git rebase`, the branch's history is rewritten — you must force-push to update the remote. This is FINE on a branch only YOU touch. It is BAD on shared branches because:
|
|
84
|
+
|
|
85
|
+
- Collaborators have pulled the old history; their next pull will conflict in confusing ways.
|
|
86
|
+
- Open PRs against the old SHAs may show garbled diffs.
|
|
87
|
+
- Reviewers lose context if they linked to a specific SHA.
|
|
88
|
+
|
|
89
|
+
Discipline:
|
|
90
|
+
- `git push --force-with-lease` on personal branches: OK.
|
|
91
|
+
- `git push --force` (without `with-lease`): never — too dangerous.
|
|
92
|
+
- Force-push to `main` / `master` / `production` / shared release branches: only with explicit authorization, ideally never.
|
|
93
|
+
|
|
94
|
+
## Cleanup: deleting merged branches
|
|
95
|
+
|
|
96
|
+
After a branch merges:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Delete locally
|
|
100
|
+
git branch -d feat/user-onboarding
|
|
101
|
+
|
|
102
|
+
# Delete remotely (most platforms auto-delete on PR merge)
|
|
103
|
+
git push origin --delete feat/user-onboarding
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
A repo with hundreds of stale branches is noise. Most platforms (GitHub, GitLab) auto-delete on merge — enable that setting if you can.
|
|
107
|
+
|
|
108
|
+
## Stale branch audit
|
|
109
|
+
|
|
110
|
+
Once a quarter (or whenever the branch list gets noisy):
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# List branches not merged to main, sorted by date
|
|
114
|
+
git for-each-ref --sort=-committerdate refs/remotes/origin --format='%(committerdate:short) %(refname:short)' | head -50
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Anything older than 60 days that hasn't merged: ask the owner — close, finish, or delete.
|
|
118
|
+
|
|
119
|
+
## Hotfix branches
|
|
120
|
+
|
|
121
|
+
When prod breaks and you need to ship fast:
|
|
122
|
+
|
|
123
|
+
1. Branch from `main` (or the production tag, if you have one).
|
|
124
|
+
2. Name: `hotfix/<short-description>` or `hotfix/<incident-id>`.
|
|
125
|
+
3. Make ONE change. Atomic commit. No "while I'm here" piggybacks.
|
|
126
|
+
4. PR to `main` (and to release branch if you have one) — fast review, fast merge, fast deploy.
|
|
127
|
+
5. Verify in production.
|
|
128
|
+
6. Add a regression test on `main` if hotfix didn't include one.
|
|
129
|
+
|
|
130
|
+
Hotfix discipline matters because the temptation is to bundle other fixes — and hotfix bundles cause the next outage.
|
|
131
|
+
|
|
132
|
+
## Stacked branches (advanced)
|
|
133
|
+
|
|
134
|
+
Sometimes you must stack branches: feature B depends on feature A, A is in review.
|
|
135
|
+
|
|
136
|
+
Options:
|
|
137
|
+
|
|
138
|
+
1. **Wait for A to merge.** Best, when possible. Start B from updated `main`.
|
|
139
|
+
2. **Stack:** branch B from A. PR B against A. When A merges, rebase B onto `main`, update PR target.
|
|
140
|
+
3. **Feature flag both A and B on `main`.** A merges first behind flag, B merges next behind flag, both flip on together.
|
|
141
|
+
|
|
142
|
+
Option 3 is the cleanest at scale. Stacking causes review and rebase pain.
|
|
143
|
+
|
|
144
|
+
## Anti-patterns
|
|
145
|
+
|
|
146
|
+
- Branch named `bruno-changes` — meaningless.
|
|
147
|
+
- Branch lives 3 weeks "because we're discussing the design" — branch isn't where design happens. Keep designing in docs/PRDs; branch when you're ready to code.
|
|
148
|
+
- `git pull` instead of `git pull --rebase` on a personal feature branch — creates merge commits in your history that you'll then need to clean up.
|
|
149
|
+
- Force-pushing to fix "a typo in the commit message" on a shared branch — use `git revert` + new commit instead.
|
|
150
|
+
- Multiple developers on the same branch without coordinating push order — non-fast-forward errors and conflicts. Pick one driver per branch or coordinate.
|