@orderful/droid 0.43.0 → 0.44.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @orderful/droid
2
2
 
3
+ ## 0.44.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#258](https://github.com/Orderful/droid/pull/258) [`2e19bbd`](https://github.com/Orderful/droid/commit/2e19bbda7146847836ccda028e1c6bf802a5215e) Thanks [@frytyler](https://github.com/frytyler)! - Add lock/unlock commands to release tool. Requires branch-lock.yml GitHub Action deployed to target repos.
8
+
9
+ ## 0.44.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#259](https://github.com/Orderful/droid/pull/259) [`f64bab2`](https://github.com/Orderful/droid/commit/f64bab250e25bb57ad9c1c2ac50246455a18eb42) Thanks [@frytyler](https://github.com/frytyler)! - Add /release merge command to merge release PRs when all checks pass and notify Slack. Fix release notes generation to compare branches instead of listing all historically merged PRs.
14
+
3
15
  ## 0.43.0
4
16
 
5
17
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "droid-release",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Release ceremony automation — create release PRs, check status, notify Slack.",
5
5
  "author": {
6
6
  "name": "Orderful",
@@ -1,6 +1,6 @@
1
1
  name: release
2
2
  description: "Release ceremony automation — create release PRs, check status, notify Slack."
3
- version: 0.1.0
3
+ version: 0.2.0
4
4
  status: alpha
5
5
 
6
6
  includes:
@@ -18,4 +18,4 @@ config_schema:
18
18
  slack_channel:
19
19
  type: string
20
20
  description: "Slack channel for release notifications"
21
- default: "#release-management"
21
+ default: "#release_management"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: release
3
- description: "Release ceremony automation"
4
- argument-hint: "[start [repo] | status | complete [repo]]"
3
+ description: "Release ceremony automation and branch locking"
4
+ argument-hint: "[start [repo] [--no-lock] | merge [repo] | lock [repo] | unlock [repo] | status | complete [repo]]"
5
5
  ---
6
6
 
7
7
  # /release
@@ -12,17 +12,25 @@ argument-hint: "[start [repo] | status | complete [repo]]"
12
12
 
13
13
  ## Examples
14
14
 
15
- - `/release start` → Create release PR for current repo
15
+ - `/release start` → Create release PR + auto-lock branch
16
16
  - `/release start orderful-workspace` → Create release PR for a specific repo
17
- - `/release status` → Check CI state across release repos
18
- - `/release complete` → Post "release complete" to Slack
17
+ - `/release start --no-lock` → Create release PR without locking
18
+ - `/release merge` → Merge release PR (checks must be green)
19
+ - `/release lock` → Manually lock the release branch
20
+ - `/release unlock` → Unlock the release branch
21
+ - `/release status` → Check CI and lock state across release repos
22
+ - `/release complete` → Post "release complete" to Slack, auto-unlock if locked
19
23
 
20
24
  ## Quick Reference
21
25
 
22
26
  ```
23
- /release start [repo] # Create release PR + notify Slack
24
- /release status # CI state across repos
25
- /release complete [repo] # Notify Slack, close out release
27
+ /release start [repo] # Create release PR + auto-lock + notify Slack
28
+ /release start --no-lock # Create release PR without locking
29
+ /release merge [repo] # Merge release PR (checks must pass)
30
+ /release lock [repo] # Manually lock release branch
31
+ /release unlock [repo] # Unlock release branch
32
+ /release status # CI + lock state across repos
33
+ /release complete [repo] # Notify Slack + auto-unlock
26
34
  ```
27
35
 
28
36
  See the **release skill** for complete documentation.
@@ -1,20 +1,21 @@
1
1
  ---
2
2
  name: release
3
- description: "Release ceremony automation for dev-to-master releases. Use when starting a release, checking release status, or completing a release. User prompts like 'start a release', 'release status', 'release complete'."
4
- argument-hint: "[start [repo] | status | complete [repo]]"
3
+ description: "Release ceremony automation for dev-to-master releases. Use when starting a release, merging a release, locking a branch, checking release status, or completing a release. User prompts like 'start a release', 'merge the release', 'lock dev', 'release status', 'unlock the branch'."
4
+ argument-hint: "[start [repo] | merge [repo] | lock [repo] | unlock [repo] | status | complete [repo]]"
5
5
  allowed-tools: [Read, Write, Glob, Grep, Bash, Edit]
6
6
  ---
7
7
 
8
8
  # Release Skill
9
9
 
10
- Automate dev → master release ceremonies: create release PRs, notify Slack, and track status.
10
+ Automate dev → master release ceremonies: create release PRs, lock branches during CI, notify Slack, and track status.
11
11
 
12
12
  ## When to Use
13
13
 
14
14
  - User wants to start a release (create PR from dev → master)
15
- - User asks about release status (open PRs, CI checks)
15
+ - User wants to lock or unlock a branch during a release
16
+ - User asks about release status (open PRs, lock state, CI checks)
16
17
  - User says "release complete" or wants to close out a release
17
- - Natural language like "start a release", "what's the release status?"
18
+ - Natural language like "start a release", "lock dev", "what's the release status?"
18
19
 
19
20
  ## When NOT to Use
20
21
 
@@ -26,6 +27,7 @@ Automate dev → master release ceremonies: create release PRs, notify Slack, an
26
27
 
27
28
  1. **`gh` CLI** — authenticated with access to target repos
28
29
  2. **Slack integration** — `droid integrations slack post` configured (optional, falls back to terminal)
30
+ 3. **`branch-lock.yml`** — GitHub Action deployed to target repo (required for auto-lock on start + lock/unlock commands)
29
31
 
30
32
  ## Configuration
31
33
 
@@ -37,7 +39,7 @@ droid config --get repos
37
39
  ```
38
40
 
39
41
  - **Repos:** Filter `repos` array for entries with `release_branch` set — these are release repos
40
- - **Slack channel:** From `tools.release.slack_channel` (default: `#release-management`)
42
+ - **Slack channel:** From `tools.release.slack_channel` (default: `#release_management`)
41
43
  - **Repo detection:** Match cwd against repo paths, or ask user if ambiguous
42
44
 
43
45
  If no repos have `release_branch` set, tell user:
@@ -47,9 +49,12 @@ If no repos have `release_branch` set, tell user:
47
49
 
48
50
  | Command | Action |
49
51
  |---------|--------|
50
- | `/release start [repo]` | Create release PR + notify Slack |
51
- | `/release status` | Check open release PRs + CI state |
52
- | `/release complete [repo]` | Post completion to Slack |
52
+ | `/release start [repo] [--no-lock]` | Create release PR + auto-lock branch + notify Slack |
53
+ | `/release merge [repo]` | Merge release PR (only if checks pass) + notify Slack |
54
+ | `/release lock [repo]` | Lock release branch (confirms first) |
55
+ | `/release unlock [repo]` | Unlock release branch |
56
+ | `/release status` | Check open release PRs + CI state + lock state |
57
+ | `/release complete [repo]` | Post completion to Slack + auto-unlock |
53
58
 
54
59
  See `references/workflows.md` for detailed step-by-step procedures and exact `gh` commands.
55
60
 
@@ -68,6 +73,8 @@ See `references/templates.md` for Slack message and PR body templates.
68
73
  |-------|--------|
69
74
  | No release repos configured | Suggest `droid repos add` with release branch |
70
75
  | `gh` CLI not authenticated | Suggest `gh auth login` |
76
+ | `branch-lock.yml` not found in repo | Tell user to add the `branch-lock.yml` workflow to the repo |
71
77
  | Slack not configured | Print message to terminal, suggest `droid integrations setup slack` |
78
+ | Branch already locked | Warn user, offer to unlock first |
72
79
  | Release PR already exists | Show existing PR, ask if user wants to proceed |
73
80
  | CI checks failing | Show status, do not auto-merge |
@@ -6,24 +6,60 @@ Slack mrkdwn and PR body templates for release notifications.
6
6
 
7
7
  All Slack messages are posted via `droid integrations slack post`. Format as Slack mrkdwn (not GitHub markdown).
8
8
 
9
- ### Release Started
9
+ ### Release Open for Review
10
10
 
11
11
  ```
12
- :rocket: *Release started — {repo_name}*
12
+ :rocket: *Release open for review — {repo_name}*
13
13
 
14
14
  *Risk:* {risk_emoji} {risk_level}
15
15
  *PR:* <{pr_url}|#{pr_number}>
16
16
  *Branch:* `{release_branch}` → `{production_branch}`
17
+ :lock: `{release_branch}` is locked — no merges until release completes
17
18
 
18
19
  {pr_summary}
19
20
 
20
21
  Posted with :droid:
21
22
  ```
22
23
 
24
+ If `--no-lock` was used, omit the lock line.
25
+
23
26
  Risk emojis:
24
27
  - Low Risk: `:large_green_circle:`
25
28
  - High Risk: `:warning:`
26
29
 
30
+ ### Release Merged
31
+
32
+ ```
33
+ :merged-pr: *Release merged — {repo_name}*
34
+
35
+ *PR:* <{pr_url}|#{pr_number}>
36
+ *Branch:* `{release_branch}` → `{production_branch}`
37
+
38
+ Monitoring deployment :eyes:
39
+
40
+ Posted with :droid:
41
+ ```
42
+
43
+ ### Branch Locked
44
+
45
+ ```
46
+ :lock: *Branch locked — {repo_name}*
47
+
48
+ `{branch}` is now read-only. No merges until unlocked.
49
+
50
+ Posted with :droid:
51
+ ```
52
+
53
+ ### Branch Unlocked
54
+
55
+ ```
56
+ :unlock: *Branch unlocked — {repo_name}*
57
+
58
+ `{branch}` is open for merges.
59
+
60
+ Posted with :droid:
61
+ ```
62
+
27
63
  ### Release Complete
28
64
 
29
65
  ```
@@ -35,6 +71,11 @@ Risk emojis:
35
71
  Posted with :droid:
36
72
  ```
37
73
 
74
+ If auto-unlocked, append:
75
+ ```
76
+ :unlock: Auto-unlocked `{release_branch}`
77
+ ```
78
+
38
79
  ---
39
80
 
40
81
  ## Release PR Body
@@ -68,12 +109,35 @@ Where `{pr_list}` is a bulleted list of merged PRs:
68
109
 
69
110
  When Slack is not configured, print a plain-text version to terminal:
70
111
 
71
- ### Release Started (terminal)
112
+ ### Release Open for Review (terminal)
72
113
  ```
73
- Release started — {repo_name}
114
+ Release open for review — {repo_name}
74
115
  Risk: {risk_level}
75
116
  PR: {pr_url}
76
117
  Branch: {release_branch} -> {production_branch}
118
+ Lock: {release_branch} locked (no merges until release completes)
119
+ ```
120
+
121
+ If `--no-lock` was used, omit the Lock line.
122
+
123
+ ### Release Merged (terminal)
124
+ ```
125
+ Release merged — {repo_name}
126
+ PR: {pr_url}
127
+ Branch: {release_branch} -> {production_branch}
128
+ Monitoring deployment...
129
+ ```
130
+
131
+ ### Branch Locked (terminal)
132
+ ```
133
+ Branch locked — {repo_name}
134
+ {branch} is now read-only
135
+ ```
136
+
137
+ ### Branch Unlocked (terminal)
138
+ ```
139
+ Branch unlocked — {repo_name}
140
+ {branch} is open for merges
77
141
  ```
78
142
 
79
143
  ### Release Complete (terminal)
@@ -7,8 +7,8 @@ Detailed step-by-step procedures for each `/release` subcommand. All commands us
7
7
  ```bash
8
8
  # 1. Read config
9
9
  SLACK_CHANNEL=$(droid config --get tools.release.slack_channel)
10
- # Default to #release-management if not set
11
- SLACK_CHANNEL="${SLACK_CHANNEL:-#release-management}"
10
+ # Default to #release_management if not set
11
+ SLACK_CHANNEL="${SLACK_CHANNEL:-#release_management}"
12
12
 
13
13
  # 2. Get repos and filter for release repos (those with release_branch set)
14
14
  droid config --get repos
@@ -25,9 +25,11 @@ git -C {repo_path} remote get-url origin
25
25
 
26
26
  ---
27
27
 
28
- ## `/release start [repo]`
28
+ ## `/release start [repo] [--no-lock]`
29
29
 
30
- Create a release PR and notify Slack.
30
+ Create a release PR, auto-lock the release branch, and notify Slack.
31
+
32
+ **Auto-lock is on by default.** The branch is locked immediately after the PR is created to prevent blind merges during CI. Pass `--no-lock` (or natural language like "start without locking") to skip.
31
33
 
32
34
  ### Steps
33
35
 
@@ -39,11 +41,14 @@ Create a release PR and notify Slack.
39
41
  ```
40
42
  If one exists, show it and ask: "A release PR already exists. Open it instead?"
41
43
 
42
- 3. **Generate release notes** — list PRs merged to the release branch since last release:
44
+ 3. **Generate release notes** — compare branches to find PRs included in this release:
43
45
  ```bash
44
- gh pr list --base {release_branch} --state merged --json number,title,author --limit 50 --repo {owner}/{repo}
46
+ # Get commits in release_branch that aren't in production_branch
47
+ gh api repos/{owner}/{repo}/compare/{production_branch}...{release_branch} --jq '.commits[].commit.message'
45
48
  ```
46
- Format as a bulleted list for the PR body.
49
+ Extract PR numbers from commit messages (e.g. `(#1234)`) and format as a bulleted list for the PR body.
50
+
51
+ **Do NOT use** `gh pr list --base {release_branch} --state merged` — that returns ALL historically merged PRs, not just the ones since the last release.
47
52
 
48
53
  4. **Ask risk level** — use AskUserQuestion:
49
54
  - Low Risk (routine release, no breaking changes)
@@ -61,7 +66,22 @@ Create a release PR and notify Slack.
61
66
  ```
62
67
  See `templates.md` for the PR body template.
63
68
 
64
- 6. **Post to Slack:**
69
+ 6. **Auto-lock branch** (unless `--no-lock`):
70
+ ```bash
71
+ gh workflow run branch-lock.yml \
72
+ -f action=lock \
73
+ -f branch={release_branch} \
74
+ --repo {owner}/{repo}
75
+ ```
76
+ Verify lock applied:
77
+ ```bash
78
+ sleep 5
79
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled'
80
+ ```
81
+ If `branch-lock.yml` is not found in the repo, warn but don't fail:
82
+ "Could not auto-lock — `branch-lock.yml` not found in `{repo_name}`. Use `/release lock` manually after adding the workflow."
83
+
84
+ 7. **Post to Slack:**
65
85
  ```bash
66
86
  node -e 'process.stdout.write(JSON.stringify({
67
87
  channel: "{slack_channel}",
@@ -69,9 +89,118 @@ Create a release PR and notify Slack.
69
89
  unfurl_links: false
70
90
  }))' | droid integrations slack post
71
91
  ```
72
- See `templates.md` for the Slack message template (release started).
92
+ See `templates.md` for the Slack message template (release open for review — includes lock status).
93
+
94
+ 8. **Confirm to user** — show PR URL, lock status, and Slack post confirmation.
95
+
96
+ ---
97
+
98
+ ## `/release merge [repo]`
99
+
100
+ Merge the release PR if all checks are green, then notify Slack.
101
+
102
+ ### Steps
103
+
104
+ 1. **Detect repo** — match cwd or ask user
105
+
106
+ 2. **Find the open release PR:**
107
+ ```bash
108
+ gh pr list --search "[RELEASE]" --state open --base {production_branch} --head {release_branch} --json number,title,url,statusCheckRollup --repo {owner}/{repo}
109
+ ```
110
+ If no open release PR found, error: "No open release PR found. Run `/release start` first."
73
111
 
74
- 7. **Confirm to user** — show PR URL and Slack post confirmation.
112
+ 3. **Check all status checks:**
113
+ Parse `statusCheckRollup` from the PR data. Every check must have `conclusion: "SUCCESS"` or `state: "SUCCESS"`.
114
+
115
+ If any checks are pending: "CI checks are still running on PR #{number}. Wait for them to finish."
116
+ If any checks are failing: "CI checks are failing on PR #{number}. Fix them before merging." Show the failing checks.
117
+
118
+ 4. **Confirm with user** — use AskUserQuestion:
119
+ "All checks are green on PR #{number}. Merge `{release_branch}` → `{production_branch}`?"
120
+
121
+ 5. **Merge the PR:**
122
+ ```bash
123
+ gh pr merge {pr_number} --merge --repo {owner}/{repo}
124
+ ```
125
+
126
+ 6. **Post to Slack** — release merged notification (see `templates.md`).
127
+
128
+ 7. **Confirm to user** — show merge confirmation and Slack post confirmation.
129
+
130
+ ---
131
+
132
+ ## `/release lock [repo]`
133
+
134
+ Manually lock the release branch. Use when you need to lock outside of `/release start` (which auto-locks).
135
+
136
+ ### Steps
137
+
138
+ 1. **Detect repo** — match cwd or ask user
139
+
140
+ 2. **Check current lock state:**
141
+ ```bash
142
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false'
143
+ ```
144
+ If already locked, warn: "Branch `{release_branch}` is already locked. Unlock first?"
145
+
146
+ 3. **Confirm with user** — use AskUserQuestion:
147
+ "Lock `{release_branch}` on `{repo_name}`? No one will be able to merge until unlocked."
148
+
149
+ 4. **Trigger the lock Action:**
150
+ ```bash
151
+ gh workflow run branch-lock.yml \
152
+ -f action=lock \
153
+ -f branch={release_branch} \
154
+ --repo {owner}/{repo}
155
+ ```
156
+
157
+ 5. **Verify lock applied** (wait a few seconds, then check):
158
+ ```bash
159
+ sleep 5
160
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled'
161
+ ```
162
+ If still not locked, check the workflow run status:
163
+ ```bash
164
+ gh run list --workflow=branch-lock.yml --limit 1 --json status,conclusion --repo {owner}/{repo}
165
+ ```
166
+
167
+ 6. **Post to Slack** — lock notification (see `templates.md`).
168
+
169
+ 7. **Confirm to user** — "Locked `{release_branch}` on `{repo_name}`."
170
+
171
+ ---
172
+
173
+ ## `/release unlock [repo]`
174
+
175
+ Unlock the release branch.
176
+
177
+ ### Steps
178
+
179
+ 1. **Detect repo** — match cwd or ask user
180
+
181
+ 2. **Check current lock state:**
182
+ ```bash
183
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false'
184
+ ```
185
+ If not locked, inform: "Branch `{release_branch}` is not currently locked."
186
+
187
+ 3. **Trigger the unlock Action:**
188
+ ```bash
189
+ gh workflow run branch-lock.yml \
190
+ -f action=unlock \
191
+ -f branch={release_branch} \
192
+ --repo {owner}/{repo}
193
+ ```
194
+
195
+ 4. **Verify unlock applied:**
196
+ ```bash
197
+ sleep 5
198
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled'
199
+ ```
200
+
201
+ 5. **Post to Slack** — unlock notification (see `templates.md`).
202
+
203
+ 6. **Confirm to user** — "Unlocked `{release_branch}` on `{repo_name}`."
75
204
 
76
205
  ---
77
206
 
@@ -90,11 +219,17 @@ Check release status across all configured release repos.
90
219
  gh pr list --search "[RELEASE]" --state open --json number,title,url,statusCheckRollup --repo {owner}/{repo}
91
220
  ```
92
221
 
222
+ **Lock state:**
223
+ ```bash
224
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false'
225
+ ```
226
+
93
227
  3. **Format and display** as a summary:
94
228
 
95
229
  ```
96
230
  {repo_name}
97
231
  Release PR: #{number} — {CI status} (green/pending/failing)
232
+ Branch lock: {release_branch} — locked/unlocked
98
233
  ```
99
234
 
100
235
  If no open release PR: "No active release"
@@ -104,7 +239,7 @@ Check release status across all configured release repos.
104
239
 
105
240
  ## `/release complete [repo]`
106
241
 
107
- Close out a release — notify Slack.
242
+ Close out a release — notify Slack and auto-unlock.
108
243
 
109
244
  ### Steps
110
245
 
@@ -124,6 +259,20 @@ Close out a release — notify Slack.
124
259
 
125
260
  3. **Post to Slack** — release complete notification (see `templates.md`).
126
261
 
127
- 4. **Confirm to user:**
262
+ 4. **Auto-unlock if locked:**
263
+ ```bash
264
+ LOCKED=$(gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false')
265
+ ```
266
+ If locked, trigger unlock:
267
+ ```bash
268
+ gh workflow run branch-lock.yml \
269
+ -f action=unlock \
270
+ -f branch={release_branch} \
271
+ --repo {owner}/{repo}
272
+ ```
273
+ Post unlock notification to Slack as well.
274
+
275
+ 5. **Confirm to user:**
128
276
  - "Release complete for `{repo_name}`"
277
+ - "Auto-unlocked `{release_branch}`" (if was locked)
129
278
  - Show Slack message confirmation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.43.0",
3
+ "version": "0.44.1",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "droid-release",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Release ceremony automation — create release PRs, check status, notify Slack.",
5
5
  "author": {
6
6
  "name": "Orderful",
@@ -1,6 +1,6 @@
1
1
  name: release
2
2
  description: "Release ceremony automation — create release PRs, check status, notify Slack."
3
- version: 0.1.0
3
+ version: 0.2.0
4
4
  status: alpha
5
5
 
6
6
  includes:
@@ -18,4 +18,4 @@ config_schema:
18
18
  slack_channel:
19
19
  type: string
20
20
  description: "Slack channel for release notifications"
21
- default: "#release-management"
21
+ default: "#release_management"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: release
3
- description: "Release ceremony automation"
4
- argument-hint: "[start [repo] | status | complete [repo]]"
3
+ description: "Release ceremony automation and branch locking"
4
+ argument-hint: "[start [repo] [--no-lock] | merge [repo] | lock [repo] | unlock [repo] | status | complete [repo]]"
5
5
  ---
6
6
 
7
7
  # /release
@@ -12,17 +12,25 @@ argument-hint: "[start [repo] | status | complete [repo]]"
12
12
 
13
13
  ## Examples
14
14
 
15
- - `/release start` → Create release PR for current repo
15
+ - `/release start` → Create release PR + auto-lock branch
16
16
  - `/release start orderful-workspace` → Create release PR for a specific repo
17
- - `/release status` → Check CI state across release repos
18
- - `/release complete` → Post "release complete" to Slack
17
+ - `/release start --no-lock` → Create release PR without locking
18
+ - `/release merge` → Merge release PR (checks must be green)
19
+ - `/release lock` → Manually lock the release branch
20
+ - `/release unlock` → Unlock the release branch
21
+ - `/release status` → Check CI and lock state across release repos
22
+ - `/release complete` → Post "release complete" to Slack, auto-unlock if locked
19
23
 
20
24
  ## Quick Reference
21
25
 
22
26
  ```
23
- /release start [repo] # Create release PR + notify Slack
24
- /release status # CI state across repos
25
- /release complete [repo] # Notify Slack, close out release
27
+ /release start [repo] # Create release PR + auto-lock + notify Slack
28
+ /release start --no-lock # Create release PR without locking
29
+ /release merge [repo] # Merge release PR (checks must pass)
30
+ /release lock [repo] # Manually lock release branch
31
+ /release unlock [repo] # Unlock release branch
32
+ /release status # CI + lock state across repos
33
+ /release complete [repo] # Notify Slack + auto-unlock
26
34
  ```
27
35
 
28
36
  See the **release skill** for complete documentation.
@@ -1,20 +1,21 @@
1
1
  ---
2
2
  name: release
3
- description: "Release ceremony automation for dev-to-master releases. Use when starting a release, checking release status, or completing a release. User prompts like 'start a release', 'release status', 'release complete'."
4
- argument-hint: "[start [repo] | status | complete [repo]]"
3
+ description: "Release ceremony automation for dev-to-master releases. Use when starting a release, merging a release, locking a branch, checking release status, or completing a release. User prompts like 'start a release', 'merge the release', 'lock dev', 'release status', 'unlock the branch'."
4
+ argument-hint: "[start [repo] | merge [repo] | lock [repo] | unlock [repo] | status | complete [repo]]"
5
5
  allowed-tools: [Read, Write, Glob, Grep, Bash, Edit]
6
6
  ---
7
7
 
8
8
  # Release Skill
9
9
 
10
- Automate dev → master release ceremonies: create release PRs, notify Slack, and track status.
10
+ Automate dev → master release ceremonies: create release PRs, lock branches during CI, notify Slack, and track status.
11
11
 
12
12
  ## When to Use
13
13
 
14
14
  - User wants to start a release (create PR from dev → master)
15
- - User asks about release status (open PRs, CI checks)
15
+ - User wants to lock or unlock a branch during a release
16
+ - User asks about release status (open PRs, lock state, CI checks)
16
17
  - User says "release complete" or wants to close out a release
17
- - Natural language like "start a release", "what's the release status?"
18
+ - Natural language like "start a release", "lock dev", "what's the release status?"
18
19
 
19
20
  ## When NOT to Use
20
21
 
@@ -26,6 +27,7 @@ Automate dev → master release ceremonies: create release PRs, notify Slack, an
26
27
 
27
28
  1. **`gh` CLI** — authenticated with access to target repos
28
29
  2. **Slack integration** — `droid integrations slack post` configured (optional, falls back to terminal)
30
+ 3. **`branch-lock.yml`** — GitHub Action deployed to target repo (required for auto-lock on start + lock/unlock commands)
29
31
 
30
32
  ## Configuration
31
33
 
@@ -37,7 +39,7 @@ droid config --get repos
37
39
  ```
38
40
 
39
41
  - **Repos:** Filter `repos` array for entries with `release_branch` set — these are release repos
40
- - **Slack channel:** From `tools.release.slack_channel` (default: `#release-management`)
42
+ - **Slack channel:** From `tools.release.slack_channel` (default: `#release_management`)
41
43
  - **Repo detection:** Match cwd against repo paths, or ask user if ambiguous
42
44
 
43
45
  If no repos have `release_branch` set, tell user:
@@ -47,9 +49,12 @@ If no repos have `release_branch` set, tell user:
47
49
 
48
50
  | Command | Action |
49
51
  |---------|--------|
50
- | `/release start [repo]` | Create release PR + notify Slack |
51
- | `/release status` | Check open release PRs + CI state |
52
- | `/release complete [repo]` | Post completion to Slack |
52
+ | `/release start [repo] [--no-lock]` | Create release PR + auto-lock branch + notify Slack |
53
+ | `/release merge [repo]` | Merge release PR (only if checks pass) + notify Slack |
54
+ | `/release lock [repo]` | Lock release branch (confirms first) |
55
+ | `/release unlock [repo]` | Unlock release branch |
56
+ | `/release status` | Check open release PRs + CI state + lock state |
57
+ | `/release complete [repo]` | Post completion to Slack + auto-unlock |
53
58
 
54
59
  See `references/workflows.md` for detailed step-by-step procedures and exact `gh` commands.
55
60
 
@@ -68,6 +73,8 @@ See `references/templates.md` for Slack message and PR body templates.
68
73
  |-------|--------|
69
74
  | No release repos configured | Suggest `droid repos add` with release branch |
70
75
  | `gh` CLI not authenticated | Suggest `gh auth login` |
76
+ | `branch-lock.yml` not found in repo | Tell user to add the `branch-lock.yml` workflow to the repo |
71
77
  | Slack not configured | Print message to terminal, suggest `droid integrations setup slack` |
78
+ | Branch already locked | Warn user, offer to unlock first |
72
79
  | Release PR already exists | Show existing PR, ask if user wants to proceed |
73
80
  | CI checks failing | Show status, do not auto-merge |
@@ -6,24 +6,60 @@ Slack mrkdwn and PR body templates for release notifications.
6
6
 
7
7
  All Slack messages are posted via `droid integrations slack post`. Format as Slack mrkdwn (not GitHub markdown).
8
8
 
9
- ### Release Started
9
+ ### Release Open for Review
10
10
 
11
11
  ```
12
- :rocket: *Release started — {repo_name}*
12
+ :rocket: *Release open for review — {repo_name}*
13
13
 
14
14
  *Risk:* {risk_emoji} {risk_level}
15
15
  *PR:* <{pr_url}|#{pr_number}>
16
16
  *Branch:* `{release_branch}` → `{production_branch}`
17
+ :lock: `{release_branch}` is locked — no merges until release completes
17
18
 
18
19
  {pr_summary}
19
20
 
20
21
  Posted with :droid:
21
22
  ```
22
23
 
24
+ If `--no-lock` was used, omit the lock line.
25
+
23
26
  Risk emojis:
24
27
  - Low Risk: `:large_green_circle:`
25
28
  - High Risk: `:warning:`
26
29
 
30
+ ### Release Merged
31
+
32
+ ```
33
+ :merged-pr: *Release merged — {repo_name}*
34
+
35
+ *PR:* <{pr_url}|#{pr_number}>
36
+ *Branch:* `{release_branch}` → `{production_branch}`
37
+
38
+ Monitoring deployment :eyes:
39
+
40
+ Posted with :droid:
41
+ ```
42
+
43
+ ### Branch Locked
44
+
45
+ ```
46
+ :lock: *Branch locked — {repo_name}*
47
+
48
+ `{branch}` is now read-only. No merges until unlocked.
49
+
50
+ Posted with :droid:
51
+ ```
52
+
53
+ ### Branch Unlocked
54
+
55
+ ```
56
+ :unlock: *Branch unlocked — {repo_name}*
57
+
58
+ `{branch}` is open for merges.
59
+
60
+ Posted with :droid:
61
+ ```
62
+
27
63
  ### Release Complete
28
64
 
29
65
  ```
@@ -35,6 +71,11 @@ Risk emojis:
35
71
  Posted with :droid:
36
72
  ```
37
73
 
74
+ If auto-unlocked, append:
75
+ ```
76
+ :unlock: Auto-unlocked `{release_branch}`
77
+ ```
78
+
38
79
  ---
39
80
 
40
81
  ## Release PR Body
@@ -68,12 +109,35 @@ Where `{pr_list}` is a bulleted list of merged PRs:
68
109
 
69
110
  When Slack is not configured, print a plain-text version to terminal:
70
111
 
71
- ### Release Started (terminal)
112
+ ### Release Open for Review (terminal)
72
113
  ```
73
- Release started — {repo_name}
114
+ Release open for review — {repo_name}
74
115
  Risk: {risk_level}
75
116
  PR: {pr_url}
76
117
  Branch: {release_branch} -> {production_branch}
118
+ Lock: {release_branch} locked (no merges until release completes)
119
+ ```
120
+
121
+ If `--no-lock` was used, omit the Lock line.
122
+
123
+ ### Release Merged (terminal)
124
+ ```
125
+ Release merged — {repo_name}
126
+ PR: {pr_url}
127
+ Branch: {release_branch} -> {production_branch}
128
+ Monitoring deployment...
129
+ ```
130
+
131
+ ### Branch Locked (terminal)
132
+ ```
133
+ Branch locked — {repo_name}
134
+ {branch} is now read-only
135
+ ```
136
+
137
+ ### Branch Unlocked (terminal)
138
+ ```
139
+ Branch unlocked — {repo_name}
140
+ {branch} is open for merges
77
141
  ```
78
142
 
79
143
  ### Release Complete (terminal)
@@ -7,8 +7,8 @@ Detailed step-by-step procedures for each `/release` subcommand. All commands us
7
7
  ```bash
8
8
  # 1. Read config
9
9
  SLACK_CHANNEL=$(droid config --get tools.release.slack_channel)
10
- # Default to #release-management if not set
11
- SLACK_CHANNEL="${SLACK_CHANNEL:-#release-management}"
10
+ # Default to #release_management if not set
11
+ SLACK_CHANNEL="${SLACK_CHANNEL:-#release_management}"
12
12
 
13
13
  # 2. Get repos and filter for release repos (those with release_branch set)
14
14
  droid config --get repos
@@ -25,9 +25,11 @@ git -C {repo_path} remote get-url origin
25
25
 
26
26
  ---
27
27
 
28
- ## `/release start [repo]`
28
+ ## `/release start [repo] [--no-lock]`
29
29
 
30
- Create a release PR and notify Slack.
30
+ Create a release PR, auto-lock the release branch, and notify Slack.
31
+
32
+ **Auto-lock is on by default.** The branch is locked immediately after the PR is created to prevent blind merges during CI. Pass `--no-lock` (or natural language like "start without locking") to skip.
31
33
 
32
34
  ### Steps
33
35
 
@@ -39,11 +41,14 @@ Create a release PR and notify Slack.
39
41
  ```
40
42
  If one exists, show it and ask: "A release PR already exists. Open it instead?"
41
43
 
42
- 3. **Generate release notes** — list PRs merged to the release branch since last release:
44
+ 3. **Generate release notes** — compare branches to find PRs included in this release:
43
45
  ```bash
44
- gh pr list --base {release_branch} --state merged --json number,title,author --limit 50 --repo {owner}/{repo}
46
+ # Get commits in release_branch that aren't in production_branch
47
+ gh api repos/{owner}/{repo}/compare/{production_branch}...{release_branch} --jq '.commits[].commit.message'
45
48
  ```
46
- Format as a bulleted list for the PR body.
49
+ Extract PR numbers from commit messages (e.g. `(#1234)`) and format as a bulleted list for the PR body.
50
+
51
+ **Do NOT use** `gh pr list --base {release_branch} --state merged` — that returns ALL historically merged PRs, not just the ones since the last release.
47
52
 
48
53
  4. **Ask risk level** — use AskUserQuestion:
49
54
  - Low Risk (routine release, no breaking changes)
@@ -61,7 +66,22 @@ Create a release PR and notify Slack.
61
66
  ```
62
67
  See `templates.md` for the PR body template.
63
68
 
64
- 6. **Post to Slack:**
69
+ 6. **Auto-lock branch** (unless `--no-lock`):
70
+ ```bash
71
+ gh workflow run branch-lock.yml \
72
+ -f action=lock \
73
+ -f branch={release_branch} \
74
+ --repo {owner}/{repo}
75
+ ```
76
+ Verify lock applied:
77
+ ```bash
78
+ sleep 5
79
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled'
80
+ ```
81
+ If `branch-lock.yml` is not found in the repo, warn but don't fail:
82
+ "Could not auto-lock — `branch-lock.yml` not found in `{repo_name}`. Use `/release lock` manually after adding the workflow."
83
+
84
+ 7. **Post to Slack:**
65
85
  ```bash
66
86
  node -e 'process.stdout.write(JSON.stringify({
67
87
  channel: "{slack_channel}",
@@ -69,9 +89,118 @@ Create a release PR and notify Slack.
69
89
  unfurl_links: false
70
90
  }))' | droid integrations slack post
71
91
  ```
72
- See `templates.md` for the Slack message template (release started).
92
+ See `templates.md` for the Slack message template (release open for review — includes lock status).
93
+
94
+ 8. **Confirm to user** — show PR URL, lock status, and Slack post confirmation.
95
+
96
+ ---
97
+
98
+ ## `/release merge [repo]`
99
+
100
+ Merge the release PR if all checks are green, then notify Slack.
101
+
102
+ ### Steps
103
+
104
+ 1. **Detect repo** — match cwd or ask user
105
+
106
+ 2. **Find the open release PR:**
107
+ ```bash
108
+ gh pr list --search "[RELEASE]" --state open --base {production_branch} --head {release_branch} --json number,title,url,statusCheckRollup --repo {owner}/{repo}
109
+ ```
110
+ If no open release PR found, error: "No open release PR found. Run `/release start` first."
73
111
 
74
- 7. **Confirm to user** — show PR URL and Slack post confirmation.
112
+ 3. **Check all status checks:**
113
+ Parse `statusCheckRollup` from the PR data. Every check must have `conclusion: "SUCCESS"` or `state: "SUCCESS"`.
114
+
115
+ If any checks are pending: "CI checks are still running on PR #{number}. Wait for them to finish."
116
+ If any checks are failing: "CI checks are failing on PR #{number}. Fix them before merging." Show the failing checks.
117
+
118
+ 4. **Confirm with user** — use AskUserQuestion:
119
+ "All checks are green on PR #{number}. Merge `{release_branch}` → `{production_branch}`?"
120
+
121
+ 5. **Merge the PR:**
122
+ ```bash
123
+ gh pr merge {pr_number} --merge --repo {owner}/{repo}
124
+ ```
125
+
126
+ 6. **Post to Slack** — release merged notification (see `templates.md`).
127
+
128
+ 7. **Confirm to user** — show merge confirmation and Slack post confirmation.
129
+
130
+ ---
131
+
132
+ ## `/release lock [repo]`
133
+
134
+ Manually lock the release branch. Use when you need to lock outside of `/release start` (which auto-locks).
135
+
136
+ ### Steps
137
+
138
+ 1. **Detect repo** — match cwd or ask user
139
+
140
+ 2. **Check current lock state:**
141
+ ```bash
142
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false'
143
+ ```
144
+ If already locked, warn: "Branch `{release_branch}` is already locked. Unlock first?"
145
+
146
+ 3. **Confirm with user** — use AskUserQuestion:
147
+ "Lock `{release_branch}` on `{repo_name}`? No one will be able to merge until unlocked."
148
+
149
+ 4. **Trigger the lock Action:**
150
+ ```bash
151
+ gh workflow run branch-lock.yml \
152
+ -f action=lock \
153
+ -f branch={release_branch} \
154
+ --repo {owner}/{repo}
155
+ ```
156
+
157
+ 5. **Verify lock applied** (wait a few seconds, then check):
158
+ ```bash
159
+ sleep 5
160
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled'
161
+ ```
162
+ If still not locked, check the workflow run status:
163
+ ```bash
164
+ gh run list --workflow=branch-lock.yml --limit 1 --json status,conclusion --repo {owner}/{repo}
165
+ ```
166
+
167
+ 6. **Post to Slack** — lock notification (see `templates.md`).
168
+
169
+ 7. **Confirm to user** — "Locked `{release_branch}` on `{repo_name}`."
170
+
171
+ ---
172
+
173
+ ## `/release unlock [repo]`
174
+
175
+ Unlock the release branch.
176
+
177
+ ### Steps
178
+
179
+ 1. **Detect repo** — match cwd or ask user
180
+
181
+ 2. **Check current lock state:**
182
+ ```bash
183
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false'
184
+ ```
185
+ If not locked, inform: "Branch `{release_branch}` is not currently locked."
186
+
187
+ 3. **Trigger the unlock Action:**
188
+ ```bash
189
+ gh workflow run branch-lock.yml \
190
+ -f action=unlock \
191
+ -f branch={release_branch} \
192
+ --repo {owner}/{repo}
193
+ ```
194
+
195
+ 4. **Verify unlock applied:**
196
+ ```bash
197
+ sleep 5
198
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled'
199
+ ```
200
+
201
+ 5. **Post to Slack** — unlock notification (see `templates.md`).
202
+
203
+ 6. **Confirm to user** — "Unlocked `{release_branch}` on `{repo_name}`."
75
204
 
76
205
  ---
77
206
 
@@ -90,11 +219,17 @@ Check release status across all configured release repos.
90
219
  gh pr list --search "[RELEASE]" --state open --json number,title,url,statusCheckRollup --repo {owner}/{repo}
91
220
  ```
92
221
 
222
+ **Lock state:**
223
+ ```bash
224
+ gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false'
225
+ ```
226
+
93
227
  3. **Format and display** as a summary:
94
228
 
95
229
  ```
96
230
  {repo_name}
97
231
  Release PR: #{number} — {CI status} (green/pending/failing)
232
+ Branch lock: {release_branch} — locked/unlocked
98
233
  ```
99
234
 
100
235
  If no open release PR: "No active release"
@@ -104,7 +239,7 @@ Check release status across all configured release repos.
104
239
 
105
240
  ## `/release complete [repo]`
106
241
 
107
- Close out a release — notify Slack.
242
+ Close out a release — notify Slack and auto-unlock.
108
243
 
109
244
  ### Steps
110
245
 
@@ -124,6 +259,20 @@ Close out a release — notify Slack.
124
259
 
125
260
  3. **Post to Slack** — release complete notification (see `templates.md`).
126
261
 
127
- 4. **Confirm to user:**
262
+ 4. **Auto-unlock if locked:**
263
+ ```bash
264
+ LOCKED=$(gh api repos/{owner}/{repo}/branches/{release_branch}/protection --jq '.lock_branch.enabled // false')
265
+ ```
266
+ If locked, trigger unlock:
267
+ ```bash
268
+ gh workflow run branch-lock.yml \
269
+ -f action=unlock \
270
+ -f branch={release_branch} \
271
+ --repo {owner}/{repo}
272
+ ```
273
+ Post unlock notification to Slack as well.
274
+
275
+ 5. **Confirm to user:**
128
276
  - "Release complete for `{repo_name}`"
277
+ - "Auto-unlocked `{release_branch}`" (if was locked)
129
278
  - Show Slack message confirmation