@chris1807/claude-kit 2.2.2 → 2.2.4
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 +1 -0
- package/package.json +1 -1
- package/templates/commands/add-to-release.md +1 -1
- package/templates/commands/cherry-pick.md +7 -5
- package/templates/commands/close-orphan-tasks.md +5 -5
- package/templates/commands/create-release.md +2 -2
- package/templates/commands/create-work-item.md +4 -4
- package/templates/commands/deploy-release.md +8 -6
- package/templates/commands/edit-work-item.md +2 -2
- package/templates/commands/implement.md +31 -13
- package/templates/commands/plan-backlog.md +4 -4
- package/templates/commands/plan-sprint.md +4 -4
- package/templates/commands/promote.md +6 -4
- package/templates/commands/quote-backlog.md +6 -6
- package/templates/commands/quote.md +3 -3
- package/templates/commands/rework.md +27 -9
- package/templates/commands/track.md +1 -1
- package/templates/infrastructure/CLAUDE-WORKFLOW.md +39 -1
package/README.md
CHANGED
|
@@ -279,6 +279,7 @@ Claude automatically:
|
|
|
279
279
|
7. Generates a UAT checklist from acceptance criteria
|
|
280
280
|
8. **Pauses for you to manually test**
|
|
281
281
|
9. Creates the PR after you confirm
|
|
282
|
+
10. **Attaches any hand-run deployment script** to the work item, with the run command in the attachment comment, so `/promote`, `/cherry-pick`, and `/deploy-release` list it for every environment. Data fixes are planned as run-once migrations the app applies by itself wherever the project has that mechanism; a hand-run script is the fallback and the plan says why
|
|
282
283
|
|
|
283
284
|
**Implementing a whole Feature:** point `/implement` at a Feature work item and it implements the child User Stories in waves driven by the `Custom.Order` field — stories with the same order value are implemented in parallel (one agent per story, each in an isolated git worktree), and the next order value starts only after the previous wave is merged and green. Everything lands on one `feature/` branch with a single review, UAT pass, and PR that links every story.
|
|
284
285
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chris1807/claude-kit",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "Claude Code starter kit for Azure DevOps teams — agents, hooks, MCP servers, slash commands, and end-to-end work item → PR → release → deploy workflow automation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@ Wait for confirmation.
|
|
|
38
38
|
|
|
39
39
|
## Step 4: Assign and Tag
|
|
40
40
|
|
|
41
|
-
Use `
|
|
41
|
+
Use `wit_work_item_write` (action `update_batch`) to:
|
|
42
42
|
1. Set the Iteration Path to `{project}\Release #{N}` on each new work item
|
|
43
43
|
2. Append `release-{N}` to each work item's tags
|
|
44
44
|
|
|
@@ -52,14 +52,16 @@ Show them with the table above, before the confirmation:
|
|
|
52
52
|
```
|
|
53
53
|
📜 Scripts to run with this promotion:
|
|
54
54
|
|
|
55
|
-
| Work Item | Script |
|
|
56
|
-
|
|
57
|
-
| AB#1234 | 2026-09-11_add_delivery_index.sql |
|
|
58
|
-
| AB#1240 | backfill-tenant-flags.ps1 |
|
|
55
|
+
| Work Item | Script | How to run |
|
|
56
|
+
|-----------|--------|------------|
|
|
57
|
+
| AB#1234 | 2026-09-11_add_delivery_index.sql | sqlcmd -S <server> -d Compass -i 2026-09-11_add_delivery_index.sql |
|
|
58
|
+
| AB#1240 | backfill-tenant-flags.ps1 | — |
|
|
59
59
|
|
|
60
60
|
Download them from the work items. This command does not run them.
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
`How to run` is the attachment's comment (`attributes.comment` on the relation) — `/implement` and `/rework` write the run command there when they attach a script. Show `—` when it is empty.
|
|
64
|
+
|
|
63
65
|
If nothing carries an attachment, say `No deployment scripts attached` on one line and move on.
|
|
64
66
|
|
|
65
67
|
**Reported, never enforced.** A script does not block the promotion and does not change the gate check: the attachment may already have been run, may be a reference copy, may not be a script at all. Listing it is the whole job — the person deploying decides.
|
|
@@ -123,7 +125,7 @@ Cherry-picking:
|
|
|
123
125
|
- **targetRefName**: `refs/heads/<target-environment-branch>`
|
|
124
126
|
- **title**: `Cherry-pick AB#1234, AB#1235 → {Environment}`
|
|
125
127
|
- **description**: List all work items with IDs and titles, and — under a `Scripts to run` heading — any deployment scripts found in Step 1b, so whoever merges sees them without opening the work items
|
|
126
|
-
3. Link all work items to the PR via `
|
|
128
|
+
3. Link all work items to the PR via `wit_work_item_link_write` (action `link_to_pull_request`)
|
|
127
129
|
|
|
128
130
|
## Step 6: Pick Who Verifies Each Group (before the merge)
|
|
129
131
|
|
|
@@ -16,7 +16,7 @@ All work items live in the **CSI Development** Azure DevOps project (see CLAUDE.
|
|
|
16
16
|
|
|
17
17
|
## Step 1: Find Orphaned Tasks
|
|
18
18
|
|
|
19
|
-
Use the Azure DevOps MCP `
|
|
19
|
+
Use the Azure DevOps MCP `wit_query` (action `wiql`) with a hierarchy link query. The Source is the parent, the Target is the child Task:
|
|
20
20
|
|
|
21
21
|
```sql
|
|
22
22
|
SELECT [System.Id]
|
|
@@ -31,11 +31,11 @@ MODE (MustContain)
|
|
|
31
31
|
|
|
32
32
|
If a **scope** was given, add the matching constraint to the `[Target]` clause (e.g. `AND [Target].[System.AreaPath] UNDER 'CSI Development\\Compass'`, or `AND [Target].[System.Title] CONTAINS 'PAY'`).
|
|
33
33
|
|
|
34
|
-
> **Note on state names:** Process templates vary. Some projects close tasks to `Done` instead of `Closed`, or use `Resolved`. If the query returns nothing or errors on an unknown state, first confirm the valid states for the Task type via `
|
|
34
|
+
> **Note on state names:** Process templates vary. Some projects close tasks to `Done` instead of `Closed`, or use `Resolved`. If the query returns nothing or errors on an unknown state, first confirm the valid states for the Task type via `wit_work_item` (action `get_type`), then adjust the `NOT IN` list and the close target in Step 3 accordingly.
|
|
35
35
|
|
|
36
36
|
If the link query is unsupported or returns no usable pairs, fall back to: query all open Tasks in scope with a flat WIQL, fetch each task's parent via `relations`, and keep only those whose parent state is in the three target states.
|
|
37
37
|
|
|
38
|
-
Fetch the matched tasks (and their parents) in a batch via `
|
|
38
|
+
Fetch the matched tasks (and their parents) in a batch via `wit_work_item` (action `get_batch`) to get titles, states, assignees, and parent context.
|
|
39
39
|
|
|
40
40
|
## Step 2: Present the Candidates
|
|
41
41
|
|
|
@@ -59,11 +59,11 @@ Otherwise, wait for explicit confirmation before proceeding. Never close tasks w
|
|
|
59
59
|
|
|
60
60
|
## Step 3: Close the Tasks
|
|
61
61
|
|
|
62
|
-
For each confirmed task, use `
|
|
62
|
+
For each confirmed task, use `wit_work_item_write` (action `update_batch`):
|
|
63
63
|
- **path**: `/fields/System.State`
|
|
64
64
|
- **value**: `Closed` (or the project's terminal Task state confirmed in Step 1)
|
|
65
65
|
|
|
66
|
-
Then add a comment on each via `
|
|
66
|
+
Then add a comment on each via `wit_work_item_comment_write` (action `add`) so the auto-close is traceable:
|
|
67
67
|
|
|
68
68
|
> Auto-closed by `/close-orphan-tasks` — parent AB#{parentId} is in state "{parentState}".
|
|
69
69
|
|
|
@@ -38,13 +38,13 @@ Create a new iteration in Azure DevOps via `work_create_iterations`:
|
|
|
38
38
|
|
|
39
39
|
## Step 4: Assign Work Items to the Release
|
|
40
40
|
|
|
41
|
-
Use `
|
|
41
|
+
Use `wit_work_item_write` (action `update_batch`) to set the Iteration Path on each work item to the new release iteration:
|
|
42
42
|
- **path**: `/fields/System.IterationPath`
|
|
43
43
|
- **value**: `{project}\Release #{N}`
|
|
44
44
|
|
|
45
45
|
## Step 5: Tag Work Items
|
|
46
46
|
|
|
47
|
-
Use `
|
|
47
|
+
Use `wit_work_item_write` (action `update_batch`) to add a release tag to each work item:
|
|
48
48
|
- **path**: `/fields/System.Tags`
|
|
49
49
|
- **value**: append `release-{N}` to existing tags
|
|
50
50
|
|
|
@@ -404,7 +404,7 @@ This applies to **User Story**. It does **not** apply to Bug or Hot Fix: neither
|
|
|
404
404
|
|
|
405
405
|
### Call the create API
|
|
406
406
|
|
|
407
|
-
Call `mcp__azure-
|
|
407
|
+
Call `mcp__azure-devops__wit_work_item_write` (action `create`) with:
|
|
408
408
|
|
|
409
409
|
- **project**: the chosen project
|
|
410
410
|
- **workItemType**: `Feature`, `Bug`, `User Story`, or `Hot Fix` (two words, exact casing)
|
|
@@ -431,7 +431,7 @@ If the `Open Questions` section is non-empty, append it as a clearly-labeled HTM
|
|
|
431
431
|
|
|
432
432
|
### Move to Dev Ready (pointed items only)
|
|
433
433
|
|
|
434
|
-
If story points were agreed in Step 4, set `System.State` to `Dev Ready` via `mcp__azure-
|
|
434
|
+
If story points were agreed in Step 4, set `System.State` to `Dev Ready` via `mcp__azure-devops__wit_work_item_write` (action `update`) **after** the item is created (a separate call — new items start in `New`, and some process templates reject a non-initial state in the create call). If the state transition is rejected, report the error and leave the state as-is — don't silently retry through intermediate states. Items created without points stay in `New`.
|
|
435
435
|
|
|
436
436
|
**Features are never moved.** A Feature stays in `New` and advances only as its child stories are verified and closed — the same rule `/implement` follows.
|
|
437
437
|
|
|
@@ -462,7 +462,7 @@ On `yes`:
|
|
|
462
462
|
```
|
|
463
463
|
|
|
464
464
|
**Wait for the user.** Revise and re-present until approved. Do not create anything before approval.
|
|
465
|
-
5. On approval, create each story with `mcp__azure-
|
|
465
|
+
5. On approval, create each story with `mcp__azure-devops__wit_work_item_write` (action `create`) in the same project, setting:
|
|
466
466
|
- `System.Description` and `Microsoft.VSTS.Common.AcceptanceCriteria` — rendered to HTML per the Step 8 rules
|
|
467
467
|
- `Custom.Order` — the wave number
|
|
468
468
|
- `Microsoft.VSTS.Scheduling.StoryPoints` — the agreed points
|
|
@@ -489,7 +489,7 @@ For a **Bug**, fetch `Microsoft.VSTS.TCM.ReproSteps` and `Microsoft.VSTS.TCM.Sys
|
|
|
489
489
|
|
|
490
490
|
For a **Hot Fix**, fetch `System.Description` and `Microsoft.VSTS.TCM.ReproSteps`, and confirm the narrative is in the former and the reproduction detail in the latter.
|
|
491
491
|
|
|
492
|
-
If a check fails the write didn't take — fix it with `
|
|
492
|
+
If a check fails the write didn't take — fix it with `wit_work_item_write` (action `update`) before reporting success. Don't report a created item you haven't read back.
|
|
493
493
|
|
|
494
494
|
After creation, report:
|
|
495
495
|
|
|
@@ -20,7 +20,7 @@ Resolve branch names from the project's `CLAUDE.md` **Pipeline Configuration** t
|
|
|
20
20
|
|
|
21
21
|
## Step 1: Read the Release
|
|
22
22
|
|
|
23
|
-
Query Azure DevOps for all work items tagged with `release-{N}` or assigned to the `Release #{N}` iteration using `search_workitem` or `
|
|
23
|
+
Query Azure DevOps for all work items tagged with `release-{N}` or assigned to the `Release #{N}` iteration using `search_workitem` or `wit_work_item` (action `list_for_iteration`).
|
|
24
24
|
|
|
25
25
|
Present the release contents:
|
|
26
26
|
|
|
@@ -93,14 +93,16 @@ Show them with the gate check, before Step 4's confirmation:
|
|
|
93
93
|
```
|
|
94
94
|
📜 Scripts to run with this promotion:
|
|
95
95
|
|
|
96
|
-
| Work Item | Script |
|
|
97
|
-
|
|
98
|
-
| AB#1234 | 2026-09-11_add_delivery_index.sql |
|
|
99
|
-
| AB#1240 | backfill-tenant-flags.ps1 |
|
|
96
|
+
| Work Item | Script | How to run |
|
|
97
|
+
|-----------|--------|------------|
|
|
98
|
+
| AB#1234 | 2026-09-11_add_delivery_index.sql | sqlcmd -S <server> -d Compass -i 2026-09-11_add_delivery_index.sql |
|
|
99
|
+
| AB#1240 | backfill-tenant-flags.ps1 | — |
|
|
100
100
|
|
|
101
101
|
Download them from the work items. This command does not run them.
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
`How to run` is the attachment's comment (`attributes.comment` on the relation) — `/implement` and `/rework` write the run command there when they attach a script. Show `—` when it is empty.
|
|
105
|
+
|
|
104
106
|
If nothing carries an attachment, say `No deployment scripts attached` on one line and move on.
|
|
105
107
|
|
|
106
108
|
**Reported, never enforced.** A script does not block the promotion and does not change the gate check: the attachment may already have been run, may be a reference copy, may not be a script at all. Listing it is the whole job — the person deploying decides.
|
|
@@ -179,7 +181,7 @@ Cherry-picking commits for Release #{N}:
|
|
|
179
181
|
- **targetRefName**: `refs/heads/<target-environment-branch>`
|
|
180
182
|
- **title**: `Release #{N} → {Environment}`
|
|
181
183
|
- **description**: List all work items included with their IDs and titles, and — under a `Scripts to run` heading — any deployment scripts found in Step 3b, so whoever merges sees them without opening the work items
|
|
182
|
-
3. Link all work items to the PR via `
|
|
184
|
+
3. Link all work items to the PR via `wit_work_item_link_write` (action `link_to_pull_request`)
|
|
183
185
|
|
|
184
186
|
## Step 8: Pick Who Verifies Each Group (before the merge)
|
|
185
187
|
|
|
@@ -230,9 +230,9 @@ Render every Markdown section to HTML first — Azure DevOps description and acc
|
|
|
230
230
|
|
|
231
231
|
Apply writes in this order, so a failure partway through leaves the most useful state behind:
|
|
232
232
|
|
|
233
|
-
1. **Update the parent item** — one `mcp__azure-
|
|
233
|
+
1. **Update the parent item** — one `mcp__azure-devops__wit_work_item_write` (action `update`) call with every changed field. Never include `System.State` for a Feature.
|
|
234
234
|
2. **Update existing children** (verdict `Update`) — one call per story, points and `Custom.Order` in the same call as the text changes. If points were set on a story still at or before `Dev Ready`, set `System.State` = `Dev Ready` in that same call.
|
|
235
|
-
3. **Create added children** — `mcp__azure-
|
|
235
|
+
3. **Create added children** — `mcp__azure-devops__wit_work_item_write` (action `create`) with description, acceptance criteria, `Custom.Order`, agreed `Microsoft.VSTS.Scheduling.StoryPoints`, and `System.AssignedTo` copied from the Feature if it has an assignee. Then link each to the Feature as a child via `mcp__azure-devops__wit_work_item_link_write` (`System.LinkTypes.Hierarchy-Reverse`), then move pointed ones to `Dev Ready` in a follow-up call.
|
|
236
236
|
4. **Re-order** any story whose only change is `Custom.Order` — one call each, touching that field alone.
|
|
237
237
|
5. **Retire** stories the user approved — set `System.State` = `Removed`, or remove the parent link, whichever the user chose. **Never delete a work item.**
|
|
238
238
|
6. **Post comments** (if the user said yes) via `mcp__azure-devops__wit_work_item_comment_write` — one short comment per edited item saying what changed and why, and naming the Feature edit that drove it (e.g. `Scope updated via edit of AB#6240: acceptance criteria 2 reworded to cover SSO; re-ordered from wave 3 to wave 2.`).
|
|
@@ -32,7 +32,7 @@ The description and acceptance criteria fields may contain embedded images (scre
|
|
|
32
32
|
|
|
33
33
|
### Comments
|
|
34
34
|
|
|
35
|
-
Read the work item comments via `
|
|
35
|
+
Read the work item comments via `wit_work_item` (action `list_comments`). Comments often contain clarifications, scope changes, or additional requirements added after the work item was created. Incorporate any relevant information from comments into your understanding of the work item.
|
|
36
36
|
|
|
37
37
|
## Step 2: Summarize and Confirm
|
|
38
38
|
|
|
@@ -98,7 +98,7 @@ Each question below is about a cost that is cheap now and expensive once the cod
|
|
|
98
98
|
| 2 | **Is each piece in the right layer?** Domain / Application / Infrastructure / API boundaries hold — no business rules in a controller, no Mongo or EF types in Domain, no HTTP concepts below API. | An `IMongoCollection<T>` parameter on a Domain method |
|
|
99
99
|
| 3 | **What is the simplest plan that still meets every AC?** State it, then adopt it or say in one line why the heavier one is needed. | An interface with exactly one implementation, added "for testability" |
|
|
100
100
|
| 4 | **What else touches the files being modified?** Name the callers. A change to a shared contract, DTO, or response shape is a breaking change until proven otherwise. | Editing a shared DTO with no note about its other consumers |
|
|
101
|
-
| 5 | **What happens to data that already exists?** New required fields, schema changes, and backfills each need an answer for rows written before this change. | A non-nullable field added with no default and no backfill |
|
|
101
|
+
| 5 | **What happens to data that already exists?** New required fields, schema changes, and backfills each need an answer for rows written before this change — and the answer should run itself: the project's run-once migration mechanism if it has one, not a script someone has to remember in every environment (see **Deployment Scripts** in CLAUDE.md). | A non-nullable field added with no default and no backfill; a `backfill.js` to run by hand on test, staging and prod when the API already runs one-time migrations at startup |
|
|
102
102
|
| 6 | **How does it fail?** Partial failure, concurrent callers, a retried request. Is the operation idempotent, and does it need to be? | A multi-step write with no story for a crash between steps |
|
|
103
103
|
| 7 | **Does it hold at real data volume?** Queries inside loops, unbounded result sets, a missing index, a list endpoint with no paging. | A `foreach` over accounts issuing one query each |
|
|
104
104
|
| 8 | **Is it safe?** Authorization on every new endpoint, and **no sensitive field** (TIN, SSN, EIN, TaxId, BankAccountNumber, RoutingNumber, any `Encrypted*`) read, logged, returned, or projected — see the sensitive-data rule in CLAUDE.md. | A new endpoint returning a whole entity because it was convenient |
|
|
@@ -131,6 +131,10 @@ Present the plan to the user:
|
|
|
131
131
|
### Files to Delete (if any)
|
|
132
132
|
- `path/to/old/file.cs` — {why it's being removed}
|
|
133
133
|
|
|
134
|
+
### Data Changes (if any)
|
|
135
|
+
- {what changes in existing data} — **automated** by {the project's run-once mechanism}: `{file}`
|
|
136
|
+
- {what changes in existing data} — **hand-run** `{script path}`, because {which Deployment Scripts reason applies}. Attached to the work item when the PR goes up.
|
|
137
|
+
|
|
134
138
|
### Unit Tests
|
|
135
139
|
- `path/to/new.tests.cs` — covers {scenario 1}, {scenario 2}, {edge case}
|
|
136
140
|
- `path/to/existing.tests.cs` — adds cases for {new behavior}
|
|
@@ -188,12 +192,14 @@ Remember the `BASE_BRANCH` — you will need it for the PR step.
|
|
|
188
192
|
|
|
189
193
|
### Move the Work Item to Active
|
|
190
194
|
|
|
191
|
-
Once the branch is created, move the work item (User Story, Bug, Hot Fix, or other single work item — never a Feature) to `Active` via `
|
|
195
|
+
Once the branch is created, move the work item (User Story, Bug, Hot Fix, or other single work item — never a Feature) to `Active` via `wit_work_item_write` (action `update`):
|
|
192
196
|
- **path**: `/fields/System.State`
|
|
193
197
|
- **value**: `Active`
|
|
194
198
|
|
|
195
199
|
If the work item is already `Active`, skip the update. If the project's process template does not have an `Active` state (the update call returns an invalid-state error), fall back in this order: `In Progress` → `Doing` → leave the current state and warn the user that the state could not be advanced automatically. Do not silently swallow the error.
|
|
196
200
|
|
|
201
|
+
After the update, **read the work item back** (`wit_work_item`, action `get`) and confirm `System.State` actually changed. If it didn't, say so explicitly — do not proceed reporting the item as in progress when the board still shows it otherwise.
|
|
202
|
+
|
|
197
203
|
### Ensure an Open Child Task Exists
|
|
198
204
|
|
|
199
205
|
The child **Task** is where hours live: Step 10 closes it and logs the hours worked when the PR goes up. So an implementation run must never proceed without one — if there's nothing to close, nothing gets logged.
|
|
@@ -264,7 +270,7 @@ Create it? (yes / edit / skip)
|
|
|
264
270
|
- `edit` → ask what to change (title or hours), revise, re-show, ask again
|
|
265
271
|
- `skip` → continue without a Task, and **warn** that Step 10 will have no Task to close and no hours will be logged for this story
|
|
266
272
|
|
|
267
|
-
On `yes`, create it with `mcp__azure-
|
|
273
|
+
On `yes`, create it with `mcp__azure-devops__wit_work_item_write` (action `create`):
|
|
268
274
|
|
|
269
275
|
- **workItemType**: `Task`
|
|
270
276
|
- **title**: `{PREFIX} - Implement: {short summary of the story}` — reuse the parent's product prefix (`COM`, `PAY`, `CDA`, …), extracted from the parent's title
|
|
@@ -275,7 +281,7 @@ On `yes`, create it with `mcp__azure-devops__wit_create_work_item`:
|
|
|
275
281
|
- `System.AssignedTo` — copy from the parent (pass the parent's `uniqueName` / email if the value is an identity object). If the parent is unassigned, leave it unset rather than failing.
|
|
276
282
|
- `System.State` — `Active`, since implementation is starting right now (fall back to the template's in-progress equivalent, or leave it at the default and note it)
|
|
277
283
|
|
|
278
|
-
Then link it as a child of the work item with `mcp__azure-
|
|
284
|
+
Then link it as a child of the work item with `mcp__azure-devops__wit_work_item_write` (action `add_child`) (or `wit_work_item_link_write` (action `link`) with `System.LinkTypes.Hierarchy-Forward`, parent → task).
|
|
279
285
|
|
|
280
286
|
If the create or link call fails, report it and ask whether to implement without a Task or stop. Don't silently continue — the user needs to know hours won't be tracked.
|
|
281
287
|
|
|
@@ -387,15 +393,26 @@ Wait for the user's response before proceeding. Do NOT create a PR until confirm
|
|
|
387
393
|
- **targetRefName**: `refs/heads/{BASE_BRANCH}` (the branch captured in Step 4)
|
|
388
394
|
- **title**: `AB#{id}: {work item title}`
|
|
389
395
|
- **labels**: `["hotfix"]` if the work item type is Hot Fix
|
|
390
|
-
3. Link the PR to the work item via `
|
|
391
|
-
4. **
|
|
392
|
-
5. **
|
|
396
|
+
3. Link the PR to the work item via `wit_work_item_link_write` (action `link_to_pull_request`)
|
|
397
|
+
4. **Attach deployment scripts** — see "Attaching Deployment Scripts" below.
|
|
398
|
+
5. **Close related Tasks and log hours** — see "Closing Related Tasks" below.
|
|
399
|
+
6. **Move the work item to `Code Review`** via `wit_work_item_write` (action `update`):
|
|
393
400
|
- **path**: `/fields/System.State`
|
|
394
401
|
- **value**: `Code Review`
|
|
395
402
|
|
|
396
403
|
If the project's process template does not have a `Code Review` state (the update call returns an invalid-state error), fall back in this order: `Resolved` → `In Review` → leave the current state and warn the user that the state could not be advanced automatically. Do not silently swallow the error.
|
|
397
404
|
|
|
398
|
-
> **Only the Task ever gets closed — never the parent.** The child Task is closed here, at PR creation (step
|
|
405
|
+
> **Only the Task ever gets closed — never the parent.** The child Task is closed here, at PR creation (step 5 above). When the PR is later completed/merged, do **not** enable Azure DevOps's "Complete associated work items" option: it transitions *every* linked work item, including the parent this PR is linked to. The parent User Story or Bug stays in `Code Review`: merging this PR into `main` deploys nothing, and promoting to `dev` deploys the code but **leaves the state alone** — the developer moves it to `Ready for Testing` once they have checked it on Dev. From there `/promote` and `/deploy-release` advance it to `Testing` → `Staging` → `Deployed` across `test → staging → prod` (see **Work Item States ↔ Environments** in CLAUDE.md).
|
|
406
|
+
|
|
407
|
+
### Attaching Deployment Scripts
|
|
408
|
+
|
|
409
|
+
A script that has to be run by hand — a SQL migration, a data backfill, a rename — is useless to the next environment if it only lives in the repo: `/promote`, `/cherry-pick` and `/deploy-release` find scripts on the **work item**, never in the diff. So every one this change adds or modifies is attached to the work item now, not left for someone to remember.
|
|
410
|
+
|
|
411
|
+
List the files the PR adds or modifies (`git diff --name-only --diff-filter=AM {BASE_BRANCH}...HEAD`) and pick out the ones a person runs by hand, once per environment. The test: if the UAT checklist, the PR, or your report tells someone to run it, it's one of these. App code, tests, build tooling and migrations the app or pipeline applies by itself are not. Each one should already be in the approved plan's **Data Changes** section with its reason for not being automated. A hand-run script that isn't there still gets attached, but say plainly that it wasn't in the plan.
|
|
412
|
+
|
|
413
|
+
Attach each one per **Deployment Scripts** in CLAUDE.md — upload through the REST API (the MCP server can only download), link it as an `AttachedFile` whose comment holds the repo path and the exact run command with the environment as a placeholder, and replace a stale same-name attachment rather than adding a second copy. Never attach a file with a connection string, key, password, or sensitive field value in it.
|
|
414
|
+
|
|
415
|
+
Report it on one line per script — `📜 Attached to AB#{id}: {file} — {how to run}` — or `No deployment scripts in this change` when there are none. A failed upload does not undo the PR; say `⚠️ NOT attached: {file} → AB#{id}` with the error and keep going.
|
|
399
416
|
|
|
400
417
|
### Closing Related Tasks
|
|
401
418
|
|
|
@@ -452,7 +469,7 @@ For each task being processed, prompt for completed hours:
|
|
|
452
469
|
|
|
453
470
|
**Wait for the user's response on every task.** Accept the suggested/current value (enter), a new numeric value, or `skip` to leave that one untouched.
|
|
454
471
|
|
|
455
|
-
Once the user has answered, update each task in a **single** `
|
|
472
|
+
Once the user has answered, update each task in a **single** `wit_work_item_write` (action `update`) call per task:
|
|
456
473
|
- `Microsoft.VSTS.Scheduling.CompletedWork` → the agreed value
|
|
457
474
|
- `Microsoft.VSTS.Scheduling.RemainingWork` → `0`
|
|
458
475
|
- `Microsoft.VSTS.Scheduling.OriginalEstimate` → only if it is still empty; set it to the agreed completed hours so the Task isn't left with no estimate at all. Never overwrite an estimate that's already there — the gap between estimate and actual is the useful signal.
|
|
@@ -557,6 +574,7 @@ Present **one combined UAT checklist grouped by story** (Step 9 rules). Wait for
|
|
|
557
574
|
|
|
558
575
|
1. Push the feature branch and create **one PR**: title `AB#{feature-id}: {feature title}`, source `feature/...`, target `BASE_BRANCH`.
|
|
559
576
|
2. Link the **Feature and every implemented story** to the PR.
|
|
560
|
-
3. Run **
|
|
561
|
-
4.
|
|
562
|
-
5.
|
|
577
|
+
3. Run **Attaching Deployment Scripts** (Step 10) over the whole feature diff. Each script goes on the **story** whose change needs it — never on the Feature, which promotions don't gate.
|
|
578
|
+
4. Run **Closing Related Tasks** (Step 10) once, covering the child Tasks of every implemented story — one combined table, then the usual per-task hour prompts. Every story that got a Task in F4 has one to close here; a story whose Task creation was skipped gets one created and closed now, as in Step 10.
|
|
579
|
+
5. Move each implemented story to `Code Review` (same fallback rules as Step 10). **Do not change the Feature's state** — the Feature is a parent container; it advances only when its child stories are verified/closed, not when the PR goes up for review.
|
|
580
|
+
6. The Step 10 closing rule applies unchanged: only child **Tasks** are ever closed — here at PR creation, never the stories and never the Feature. Don't enable "Complete associated work items" when the PR is merged; it would transition the stories and the Feature along with the Tasks.
|
|
@@ -27,7 +27,7 @@ If the user types `list`, call `mcp__azure-devops__core_list_projects` and prese
|
|
|
27
27
|
|
|
28
28
|
## Step 2: Query the Backlog
|
|
29
29
|
|
|
30
|
-
Run a WIQL query via `mcp__azure-
|
|
30
|
+
Run a WIQL query via `mcp__azure-devops__wit_query` (action `wiql`) to find candidate stories. "Backlog" means items at the **root iteration path** (not assigned to any sprint).
|
|
31
31
|
|
|
32
32
|
```sql
|
|
33
33
|
SELECT [System.Id], [System.Title], [System.State],
|
|
@@ -55,7 +55,7 @@ and stop.
|
|
|
55
55
|
|
|
56
56
|
## Step 3: Filter Out Stories That Already Have Child Tasks
|
|
57
57
|
|
|
58
|
-
For each candidate from Step 2, fetch the work item with `mcp__azure-
|
|
58
|
+
For each candidate from Step 2, fetch the work item with `mcp__azure-devops__wit_work_item` (action `get`) expanding `relations`. If the item has any `System.LinkTypes.Hierarchy-Forward` relation pointing at a `Task`, mark it as **already broken down** and skip it.
|
|
59
59
|
|
|
60
60
|
Present a one-line summary of what was skipped:
|
|
61
61
|
|
|
@@ -175,7 +175,7 @@ Approve? (yes / edit / skip / cancel-all)
|
|
|
175
175
|
|
|
176
176
|
### 5e. Create the child task
|
|
177
177
|
|
|
178
|
-
Create the approved task with `mcp__azure-
|
|
178
|
+
Create the approved task with `mcp__azure-devops__wit_work_item_write` (action `create`):
|
|
179
179
|
|
|
180
180
|
- **project**: the chosen project
|
|
181
181
|
- **workItemType**: `Task`
|
|
@@ -187,7 +187,7 @@ Create the approved task with `mcp__azure-devops__wit_create_work_item`:
|
|
|
187
187
|
- `System.AreaPath` — copy from the parent
|
|
188
188
|
- `System.AssignedTo` — copy from the parent (pass the parent's `uniqueName` / email if its `System.AssignedTo` value is an identity object). If the parent is unassigned, leave this field unset rather than failing.
|
|
189
189
|
|
|
190
|
-
Then link the new task as a child of the parent with `mcp__azure-
|
|
190
|
+
Then link the new task as a child of the parent with `mcp__azure-devops__wit_work_item_write` (action `add_child`) (or fall back to `wit_work_item_link_write` (action `link`) with link type `System.LinkTypes.Hierarchy-Forward` from parent → task).
|
|
191
191
|
|
|
192
192
|
If the create or link call fails, report the failure and ask the user whether to continue with the next story or abort the loop.
|
|
193
193
|
|
|
@@ -44,7 +44,7 @@ and stop.
|
|
|
44
44
|
|
|
45
45
|
## Step 3: Query the Sprint
|
|
46
46
|
|
|
47
|
-
Run a WIQL query via `mcp__azure-
|
|
47
|
+
Run a WIQL query via `mcp__azure-devops__wit_query` (action `wiql`) using the **literal iteration path** from Step 2:
|
|
48
48
|
|
|
49
49
|
```sql
|
|
50
50
|
SELECT [System.Id], [System.Title], [System.State],
|
|
@@ -73,7 +73,7 @@ and stop.
|
|
|
73
73
|
|
|
74
74
|
## Step 4: Filter Out Items That Already Have Child Tasks
|
|
75
75
|
|
|
76
|
-
For each candidate from Step 3, fetch the work item with `mcp__azure-
|
|
76
|
+
For each candidate from Step 3, fetch the work item with `mcp__azure-devops__wit_work_item` (action `get`) expanding `relations`. If the item has any `System.LinkTypes.Hierarchy-Forward` relation pointing at a `Task`, mark it as **already broken down** and skip it.
|
|
77
77
|
|
|
78
78
|
Present a one-line summary of what was skipped:
|
|
79
79
|
|
|
@@ -196,7 +196,7 @@ For unpointed items, the header line reads `(no points — estimated from descri
|
|
|
196
196
|
|
|
197
197
|
### 6e. Create the child task
|
|
198
198
|
|
|
199
|
-
Create the approved task with `mcp__azure-
|
|
199
|
+
Create the approved task with `mcp__azure-devops__wit_work_item_write` (action `create`):
|
|
200
200
|
|
|
201
201
|
- **project**: the chosen project
|
|
202
202
|
- **workItemType**: `Task`
|
|
@@ -208,7 +208,7 @@ Create the approved task with `mcp__azure-devops__wit_create_work_item`:
|
|
|
208
208
|
- `System.AreaPath` — copy from the parent
|
|
209
209
|
- `System.AssignedTo` — copy from the parent (pass the parent's `uniqueName` / email if its `System.AssignedTo` value is an identity object). If the parent is unassigned, leave this field unset rather than failing.
|
|
210
210
|
|
|
211
|
-
Then link the new task as a child of the parent with `mcp__azure-
|
|
211
|
+
Then link the new task as a child of the parent with `mcp__azure-devops__wit_work_item_write` (action `add_child`) (or fall back to `wit_work_item_link_write` (action `link`) with link type `System.LinkTypes.Hierarchy-Forward` from parent → task).
|
|
212
212
|
|
|
213
213
|
If the create or link call fails, report the failure and ask the user whether to continue with the next item or abort the loop.
|
|
214
214
|
|
|
@@ -98,14 +98,16 @@ Show them with the gate check, before the confirmation:
|
|
|
98
98
|
```
|
|
99
99
|
📜 Scripts to run with this promotion:
|
|
100
100
|
|
|
101
|
-
| Work Item | Script |
|
|
102
|
-
|
|
103
|
-
| AB#1234 | 2026-09-11_add_delivery_index.sql |
|
|
104
|
-
| AB#1240 | backfill-tenant-flags.ps1 |
|
|
101
|
+
| Work Item | Script | How to run |
|
|
102
|
+
|-----------|--------|------------|
|
|
103
|
+
| AB#1234 | 2026-09-11_add_delivery_index.sql | sqlcmd -S <server> -d Compass -i 2026-09-11_add_delivery_index.sql |
|
|
104
|
+
| AB#1240 | backfill-tenant-flags.ps1 | — |
|
|
105
105
|
|
|
106
106
|
Download them from the work items. This command does not run them.
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
`How to run` is the attachment's comment (`attributes.comment` on the relation) — `/implement` and `/rework` write the run command there when they attach a script. Show `—` when it is empty.
|
|
110
|
+
|
|
109
111
|
If nothing carries an attachment, say `No deployment scripts attached` on one line and move on.
|
|
110
112
|
|
|
111
113
|
**Reported, never enforced.** A script does not block the promotion and does not change the gate check: the attachment may already have been run, may be a reference copy, may not be a script at all. Listing it is the whole job — the person deploying decides.
|
|
@@ -91,7 +91,7 @@ If the user types `list`, call `mcp__azure-devops__core_list_projects` and prese
|
|
|
91
91
|
|
|
92
92
|
## Step 2: Query the Backlog
|
|
93
93
|
|
|
94
|
-
Run a WIQL query via `mcp__azure-
|
|
94
|
+
Run a WIQL query via `mcp__azure-devops__wit_query` (action `wiql`) to find candidate items. "Backlog" means items at the **root iteration path** (not assigned to any sprint).
|
|
95
95
|
|
|
96
96
|
```sql
|
|
97
97
|
SELECT [System.Id], [System.Title], [System.State],
|
|
@@ -203,7 +203,7 @@ For each item in the batch, do all of 3a–3e before presenting anything. Collec
|
|
|
203
203
|
|
|
204
204
|
### 3a. Fetch the full work item
|
|
205
205
|
|
|
206
|
-
Fetch with `mcp__azure-
|
|
206
|
+
Fetch with `mcp__azure-devops__wit_work_item` (action `get`) expanding `relations`. Capture: title, type, description, acceptance criteria, tags, `System.CreatedBy` (display name + unique name — needed for the comment draft), child items, and any linked PRs, commits, or branches.
|
|
207
207
|
|
|
208
208
|
### 3b. Completeness review
|
|
209
209
|
|
|
@@ -368,8 +368,8 @@ Approve? (all / numbers e.g. "1,2,4" / edit N / skip N / apply rewrite N / cance
|
|
|
368
368
|
|
|
369
369
|
Only for approved items, in batch order:
|
|
370
370
|
|
|
371
|
-
1. **Set Story Points and move to Dev Ready** (items with a proposed number): in one `mcp__azure-
|
|
372
|
-
2. **Send unquotable User Stories back to Design Review** (approved `User Story` items with **no** proposed points, where the blocker is missing information): set `System.State` = `Design Review` via `mcp__azure-
|
|
371
|
+
1. **Set Story Points and move to Dev Ready** (items with a proposed number): in one `mcp__azure-devops__wit_work_item_write` (action `update`) call, set `Microsoft.VSTS.Scheduling.StoryPoints` **and** `System.State` = `Dev Ready`. The state change applies only to `User Story`, `Bug`, and `Hot Fix` types, and never moves an item backward — if an item is somehow already past Dev Ready, set the points only and note it. Touch no other field — assignee, iteration, and tags stay as they are.
|
|
372
|
+
2. **Send unquotable User Stories back to Design Review** (approved `User Story` items with **no** proposed points, where the blocker is missing information): set `System.State` = `Design Review` via `mcp__azure-devops__wit_work_item_write` (action `update`). Do this **before** posting the comment in step 4, so the creator's notification arrives with the item already back in their queue. Rules:
|
|
373
373
|
|
|
374
374
|
- **`User Story` only.** Do not attempt this on a `Bug` or a `Hot Fix` — those types have no `Design Review` state, so the update fails at runtime in the middle of the batch. They take step 3 instead.
|
|
375
375
|
- Only from `Design Approved` — **never move an item backward past the design stage.** An item at `Dev Ready`, or anything at `Active` or later (`Active`, `Code Review`, `Ready for Testing`, `Testing`, `Ready for Staging`, `Staging`, `Ready to Deploy`, …), keeps its state; note it in the summary instead.
|
|
@@ -387,8 +387,8 @@ Only for approved items, in batch order:
|
|
|
387
387
|
- Skip items whose only finding is **needs to be split** — nothing is missing, so no tag.
|
|
388
388
|
- Touch no other field — points stay empty; assignee, iteration, and state stay as they are.
|
|
389
389
|
|
|
390
|
-
4. **Post the comment** (items with an approved draft): add it with `mcp__azure-
|
|
391
|
-
5. **Apply the rewrite** (only items the user marked `apply rewrite N`): update `System.Description` (and `System.Title` if the rewrite included one) via `
|
|
390
|
+
4. **Post the comment** (items with an approved draft): add it with `mcp__azure-devops__wit_work_item_comment_write` (action `add`) (or the server's work-item comment tool). Use the mention syntax the server supports so the creator is notified; otherwise lead with their display name as drafted. For a story that just moved to Design Review, the comment must say so — the creator needs to know why it left their Design Approved column. For a bug that just got the `needs-info` tag, the comment must say that too, and say that removing the tag re-queues it.
|
|
391
|
+
5. **Apply the rewrite** (only items the user marked `apply rewrite N`): update `System.Description` (and `System.Title` if the rewrite included one) via `wit_work_item_write` (action `update`), and adjust the comment to say the rewrite was applied ("rewrote the description/AC per the above — please review") rather than suggesting it. Never apply a rewrite the user didn't explicitly mark.
|
|
392
392
|
|
|
393
393
|
**Guard on `Microsoft.VSTS.Common.AcceptanceCriteria`:** write it only if the item's AC field was **non-empty** when fetched in 3a. Re-check the fetched value at write time — if it was blank, drop AC from the update payload and write the other fields. Blank means no criteria at all: empty string, whitespace, an empty HTML shell like `<div></div>` or `<p><br></p>`, **or the process template's placeholder tip text** (`💡 Tip: Add "@serena rewrite" to Description for AI suggestions Define acceptance criteria: - [ ] - [ ] - [ ]`). The placeholder is the one that bites — it is a stored, non-empty field value that means the exact opposite of what its length suggests.
|
|
394
394
|
|
|
@@ -111,7 +111,7 @@ After displaying the estimate, ask the user: *"Want me to set Story Points = {n}
|
|
|
111
111
|
|
|
112
112
|
Only update the work item if the user explicitly says yes. Do not modify anything otherwise.
|
|
113
113
|
|
|
114
|
-
When the user agrees, in the **same** `
|
|
114
|
+
When the user agrees, in the **same** `wit_work_item_write` (action `update`) call:
|
|
115
115
|
|
|
116
116
|
1. Set `Microsoft.VSTS.Scheduling.StoryPoints` to the agreed value (the user's number wins if they adjusted it).
|
|
117
117
|
2. Set `System.State` to `Dev Ready` — **but only if** the work item type is `User Story`, `Bug`, or `Hot Fix` **and** the item is not already past Dev Ready in the workflow (e.g. `Active`, `Code Review`, `Testing`, `Staging`, `Ready to Deploy`). Never move an item backward — if it's already past Dev Ready, set the points only and mention the state was left alone.
|
|
@@ -134,7 +134,7 @@ In the CSI Development process template, `Bug` and `Hot Fix` have **no design st
|
|
|
134
134
|
|
|
135
135
|
> *"I can't size this until {what's missing}. Want me to move AB#{id} back to Design Review so it's out of the next `/quote-backlog` sweep until the creator responds?"*
|
|
136
136
|
|
|
137
|
-
**Wait for the user.** Only update the work item if they say yes. Then set `System.State` = `Design Review` with `mcp__azure-
|
|
137
|
+
**Wait for the user.** Only update the work item if they say yes. Then set `System.State` = `Design Review` with `mcp__azure-devops__wit_work_item_write` (action `update`). Rules:
|
|
138
138
|
|
|
139
139
|
- **`User Story` only** — never a Bug, Hot Fix, Feature, or Task. A Bug takes 5b; the update would fail on it anyway.
|
|
140
140
|
- **Never move an item backward past the design stage.** Only from `Design Approved`; if it's already `Dev Ready` or anything at `Active` or later (`Active`, `Code Review`, `Ready for Testing`, `Testing`, `Ready for Staging`, `Staging`, `Ready to Deploy`, …), leave the state alone and say so — someone is already working on it, and a state bounce there does real damage.
|
|
@@ -146,7 +146,7 @@ In the CSI Development process template, `Bug` and `Hot Fix` have **no design st
|
|
|
146
146
|
|
|
147
147
|
> *"I can't size this until {what's missing} — and a Bug has no design state to send it back to. Want me to tag AB#{id} `needs-info` so it drops out of the next `/quote-backlog` sweep? Removing the tag is what re-queues it — a reply alone leaves it excluded, though `/quote-backlog`'s stale-tag audit will surface it."*
|
|
148
148
|
|
|
149
|
-
**Wait for the user.** Only update the work item if they say yes. Then add the tag with `mcp__azure-
|
|
149
|
+
**Wait for the user.** Only update the work item if they say yes. Then add the tag with `mcp__azure-devops__wit_work_item_write` (action `update`). Rules:
|
|
150
150
|
|
|
151
151
|
- **Append to `System.Tags`, never replace it.** Take the current tag list captured in Step 1, append `needs-info`, and write the whole semicolon-separated list back. Writing the field bare wipes tags someone else set — `release-{N}`, triage labels, scope hints.
|
|
152
152
|
- Already tagged `needs-info` → no-op; say it's already there.
|
|
@@ -36,13 +36,13 @@ Save the PR's `creationDate` as `LAST_PR_DATE` — everything after this timesta
|
|
|
36
36
|
|
|
37
37
|
### New Comments
|
|
38
38
|
|
|
39
|
-
Read the work item comments via `
|
|
39
|
+
Read the work item comments via `wit_work_item` (action `list_comments`). Filter to only comments created **after** `LAST_PR_DATE`. These contain the rework feedback.
|
|
40
40
|
|
|
41
41
|
For each new comment, check for embedded images (`<img>` tags with `src` URLs pointing to Azure DevOps attachments). **Download and view every embedded image** using WebFetch — they often contain screenshots of bugs, visual issues, or annotated UI showing what needs to change.
|
|
42
42
|
|
|
43
43
|
### Description & Acceptance Criteria Changes
|
|
44
44
|
|
|
45
|
-
Read the work item revisions via `
|
|
45
|
+
Read the work item revisions via `wit_work_item` (action `list_revisions`). Check if the **description** or **acceptance criteria** fields were modified **after** `LAST_PR_DATE`.
|
|
46
46
|
|
|
47
47
|
- If changed: extract the **current** description and acceptance criteria, and note what was added or modified
|
|
48
48
|
- If unchanged: still read the current description and acceptance criteria — a comment may reference something that was in the original requirements but missing from the implementation
|
|
@@ -123,7 +123,7 @@ Each question below is about a cost that is cheap now and expensive once the cod
|
|
|
123
123
|
| 2 | **Is each piece in the right layer?** Domain / Application / Infrastructure / API boundaries hold — no business rules in a controller, no Mongo or EF types in Domain, no HTTP concepts below API. | An `IMongoCollection<T>` parameter on a Domain method |
|
|
124
124
|
| 3 | **What is the simplest plan that still meets every AC?** State it, then adopt it or say in one line why the heavier one is needed. | An interface with exactly one implementation, added "for testability" |
|
|
125
125
|
| 4 | **What else touches the files being modified?** Name the callers. A change to a shared contract, DTO, or response shape is a breaking change until proven otherwise. | Editing a shared DTO with no note about its other consumers |
|
|
126
|
-
| 5 | **What happens to data that already exists?** New required fields, schema changes, and backfills each need an answer for rows written before this change. | A non-nullable field added with no default and no backfill |
|
|
126
|
+
| 5 | **What happens to data that already exists?** New required fields, schema changes, and backfills each need an answer for rows written before this change — and the answer should run itself: the project's run-once migration mechanism if it has one, not a script someone has to remember in every environment (see **Deployment Scripts** in CLAUDE.md). | A non-nullable field added with no default and no backfill; a `backfill.js` to run by hand on test, staging and prod when the API already runs one-time migrations at startup |
|
|
127
127
|
| 6 | **How does it fail?** Partial failure, concurrent callers, a retried request. Is the operation idempotent, and does it need to be? | A multi-step write with no story for a crash between steps |
|
|
128
128
|
| 7 | **Does it hold at real data volume?** Queries inside loops, unbounded result sets, a missing index, a list endpoint with no paging. | A `foreach` over accounts issuing one query each |
|
|
129
129
|
| 8 | **Is it safe?** Authorization on every new endpoint, and **no sensitive field** (TIN, SSN, EIN, TaxId, BankAccountNumber, RoutingNumber, any `Encrypted*`) read, logged, returned, or projected — see the sensitive-data rule in CLAUDE.md. | A new endpoint returning a whole entity because it was convenient |
|
|
@@ -158,6 +158,10 @@ Present the plan to the user:
|
|
|
158
158
|
### Files to Delete (if any)
|
|
159
159
|
- `path/to/old/file.cs` — {why it's being removed}
|
|
160
160
|
|
|
161
|
+
### Data Changes (if any)
|
|
162
|
+
- {what changes in existing data} — **automated** by {the project's run-once mechanism}: `{file}`
|
|
163
|
+
- {what changes in existing data} — **hand-run** `{script path}`, because {which Deployment Scripts reason applies}. Attached to the work item when the PR goes up.
|
|
164
|
+
|
|
161
165
|
### Unit Tests
|
|
162
166
|
- `path/to/new.tests.cs` — covers {scenario the rework adds or fixes}
|
|
163
167
|
- `path/to/existing.tests.cs` — updates assertions for {changed behavior}
|
|
@@ -236,7 +240,7 @@ Enter the estimated hours for this task (press enter to accept {n}):
|
|
|
236
240
|
|
|
237
241
|
If the user provided hours (suggested or overridden):
|
|
238
242
|
|
|
239
|
-
1. Call `mcp__azure-
|
|
243
|
+
1. Call `mcp__azure-devops__wit_work_item_write` (action `create`):
|
|
240
244
|
- **workItemType**: `Task`
|
|
241
245
|
- **title**: `Rework AB#{id} — round {N}`
|
|
242
246
|
- **fields**: JSON Patch document setting:
|
|
@@ -262,8 +266,9 @@ If the user provided hours (suggested or overridden):
|
|
|
262
266
|
- `System.AreaPath` — same as the parent
|
|
263
267
|
- `System.IterationPath` — same as the parent
|
|
264
268
|
- `System.AssignedTo` — same as the parent (copy the parent's `System.AssignedTo` value; pass the `uniqueName` / email if the parent's value is an identity object). If the parent is unassigned, leave this field unset rather than failing.
|
|
269
|
+
- `System.State` — `Active`, since the rework starts right now (fall back to the template's in-progress equivalent, or leave it at the default and note it). A rework Task left at `New` while the work is underway misreports the board.
|
|
265
270
|
|
|
266
|
-
2. Link the new Task as a child of the parent work item via `
|
|
271
|
+
2. Link the new Task as a child of the parent work item via `wit_work_item_link_write` (action `link`):
|
|
267
272
|
- **type**: `Child` (the parent → child link from the parent's perspective; equivalent to `Parent` from the task's perspective)
|
|
268
273
|
- **source**: parent work item ID
|
|
269
274
|
- **target**: new task ID
|
|
@@ -282,6 +287,18 @@ The work item already has a branch from the previous PR. Switch to it:
|
|
|
282
287
|
|
|
283
288
|
If the PR was completed/merged and the branch was deleted, create a new branch from the PR's target branch following the same naming convention as `/implement` Step 4.
|
|
284
289
|
|
|
290
|
+
### Move the Work Item to Active
|
|
291
|
+
|
|
292
|
+
Once you are on the branch, move the work item (User Story, Bug, Hot Fix, or other single work item — **never** a Feature) to `Active` via `wit_work_item_write` (action `update`):
|
|
293
|
+
- **path**: `/fields/System.State`
|
|
294
|
+
- **value**: `Active`
|
|
295
|
+
|
|
296
|
+
Rework restarts implementation, so the item must leave `Code Review` / `Rework` and go back to in-progress for the duration of this round — Step 12 hands it back to `Code Review` when the fixes are pushed. Without this the item sits in a review state while it is actively being worked, and the board lies.
|
|
297
|
+
|
|
298
|
+
If the work item is already `Active`, skip the update. If the project's process template does not have an `Active` state (the update call returns an invalid-state error), fall back in this order: `In Progress` → `Doing` → leave the current state and warn the user that the state could not be advanced automatically. Do not silently swallow the error.
|
|
299
|
+
|
|
300
|
+
After the update, **read the work item back** (`wit_work_item`, action `get`) and confirm `System.State` actually changed. If it didn't, say so explicitly — do not proceed reporting the item as in progress when the board still shows it otherwise.
|
|
301
|
+
|
|
285
302
|
## Step 7: Implement
|
|
286
303
|
|
|
287
304
|
1. **Implement** the rework using backend and/or frontend agents according to the approved plan
|
|
@@ -375,9 +392,10 @@ Wait for the user's response before proceeding. Do NOT push until confirmed.
|
|
|
375
392
|
## Step 12: Push and Update
|
|
376
393
|
|
|
377
394
|
1. Push the changes: `git push`
|
|
378
|
-
2. **Do not post a rework summary comment.** Do not add a summary of what changed to the work item Discussion (`
|
|
379
|
-
3. **
|
|
380
|
-
4. **
|
|
395
|
+
2. **Do not post a rework summary comment.** Do not add a summary of what changed to the work item Discussion (`wit_work_item_comment_write` (action `add`)) or as a PR thread. The pushed commits and the PR diff are the record of what changed — a prose summary duplicates them and clutters the work item. If the reviewer left specific PR comment threads, reply on those threads directly (that is what `/resolve-feedback` and `/fix-review` do); otherwise post nothing.
|
|
396
|
+
3. **Attach deployment scripts.** Run `/implement`'s **Attaching Deployment Scripts** step against this branch — diff it against the PR's target branch (`git diff --name-only --diff-filter=AM {target}...HEAD`), so a script the original PR added and never attached is caught too. A script this rework changed is already attached in its old form: replace that attachment, don't add a second copy — a promotion that lists both versions invites someone to run the stale one. Same rules and report lines as in `/implement` (see **Deployment Scripts** in CLAUDE.md).
|
|
397
|
+
4. **Close related Tasks and log hours** — see "Closing Related Tasks" below. This includes the rework Task created in Step 5 as well as any other child Tasks that became `Completed` as a result of this rework round.
|
|
398
|
+
5. **Move the work item back to `Code Review`** via `wit_work_item_write` (action `update`):
|
|
381
399
|
- **path**: `/fields/System.State`
|
|
382
400
|
- **value**: `Code Review`
|
|
383
401
|
|
|
@@ -440,7 +458,7 @@ For each task being processed, prompt for completed hours:
|
|
|
440
458
|
|
|
441
459
|
**Wait for the user's response on every task.** Accept the suggested/current value (enter), a new numeric value, or `skip` to leave that one untouched.
|
|
442
460
|
|
|
443
|
-
Once the user has answered, update each task via `
|
|
461
|
+
Once the user has answered, update each task via `wit_work_item_write` (action `update`):
|
|
444
462
|
- `Microsoft.VSTS.Scheduling.CompletedWork` → the agreed value
|
|
445
463
|
- `Microsoft.VSTS.Scheduling.RemainingWork` → `0`
|
|
446
464
|
- `System.State` → `Closed` (fall back to `Done` if the project's task template uses Agile; warn if neither is valid)
|
|
@@ -49,7 +49,7 @@ Deployed to:
|
|
|
49
49
|
Catch these up? (yes/no)
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
If the user says yes, set `System.State` on each lagging item to the expected state via `
|
|
52
|
+
If the user says yes, set `System.State` on each lagging item to the expected state via `wit_work_item_write` (action `update`) — never backward, never on a Feature or Task — and report `Was → Now`.
|
|
53
53
|
|
|
54
54
|
## If Pipeline (e.g., `/track pipeline` or `/track build`):
|
|
55
55
|
|
|
@@ -185,10 +185,48 @@ How the commands use this table:
|
|
|
185
185
|
|
|
186
186
|
#### Deployment Scripts
|
|
187
187
|
|
|
188
|
-
A SQL migration
|
|
188
|
+
A SQL migration, a data backfill, a rename script — anything someone has to **run by hand** against an environment — is attached to the work item it belongs to, and it has to run in **every** environment that item's code lands in — test, then staging, then prod. The promotion carries the code, never the script.
|
|
189
189
|
|
|
190
190
|
`/promote`, `/cherry-pick` and `/deploy-release` therefore read the carried items' attachments (`wit_work_item` `action: get`, `expand: "Relations"`) and list any that look like something to run: an `AttachedFile` whose extension is **not** a document, image or video (`.docx .xlsx .csv .pdf .png .jpg .mp4` and friends). Everything else counts — the rule is loose on purpose, because a filename glanced at and dismissed costs seconds while a migration nobody ran costs an environment. The list appears before the confirmation, in the PR description, and again once the pipeline is green, which is when it actually has to be run.
|
|
191
191
|
|
|
192
|
+
**Automate first — a hand-run script is the fallback.** When a change needs existing data corrected, backfilled or renamed, Claude plans it through the project's own run-once migration mechanism if it has one: a seeder that records finished migrations (COMPASS's `DataSeeder` and its `dataMigrations` collection), EF Core migrations applied at startup, DbUp, a migration step the pipeline already runs. Those run by themselves on every environment's deploy, after the new code is live, with the app's own connection string — nothing to remember, nothing to attach. A hand-run script is right only when the change can't live there, and the plan names which reason applies:
|
|
193
|
+
|
|
194
|
+
- the project has no such mechanism — building one is its own work item, never folded into this one;
|
|
195
|
+
- the script creates the database or tenant the app would run in (provisioning);
|
|
196
|
+
- it touches something outside the app's own data stores (Azure resources, another system);
|
|
197
|
+
- a person has to check the result before it is applied.
|
|
198
|
+
|
|
199
|
+
**Claude always attaches the hand-run script it writes.** Committing it to the repo is not enough: the promotion commands look for scripts on the work item, never in the repo, so an unattached script is reported as `No deployment scripts attached` and goes unrun in staging or prod. Whenever Claude writes one — in `/implement`, `/rework`, or any other session — or tells someone to run one that isn't attached yet, it attaches it before finishing:
|
|
200
|
+
|
|
201
|
+
- **Which files.** A file in the change that a person runs by hand, once per environment: `.sql`, a Mongo script, a `.sh` / `.ps1` / `.py` / `.js` data fix. The test: if the UAT checklist, the PR, or Claude's report tells someone to run it, it gets attached. Not app code, not tests or build tooling, not a migration the app or the pipeline applies by itself.
|
|
202
|
+
- **Which work item.** The one whose change needs it. In a Feature run, the child story — never the Feature.
|
|
203
|
+
- **The comment says how to run it.** The attachment comment carries the repo path and the exact command, with the environment as a placeholder (e.g. `scripts/tenant-provisioning/rename-manager-roles.sh — after each environment's deploy: RMR_CONFIRM='apply <Env>' scripts/…/rename-manager-roles.sh apply <Env>`). Whoever deploys to prod next month won't have this session.
|
|
204
|
+
- **No secrets in the file.** Never attach a script with a connection string, key, password, or sensitive field value in it — the script reads those from the environment. If it can't, stop and tell the user instead of attaching.
|
|
205
|
+
- **Replace, don't duplicate.** If an attachment with the same filename is already on the item, compare it with the repo copy (`curl` the attachment's `url` into `diff`). Identical → leave it. Different → swap it in one PATCH (`remove` the old relation by its index, then `add` the new one), so a promotion never lists two versions of one script.
|
|
206
|
+
|
|
207
|
+
The Azure DevOps MCP server can only *download* attachments, so the upload goes through the REST API with an `az` token. The org and project GUID come from the work item's own `url` (`https://dev.azure.com/{org}/{projectId}/_apis/wit/workItems/{id}`):
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
TOKEN=$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)
|
|
211
|
+
BASE="https://dev.azure.com/{org}/{projectId}/_apis/wit"
|
|
212
|
+
|
|
213
|
+
# 1. Upload the bytes — the response JSON carries the attachment "url"
|
|
214
|
+
curl -sS -X POST "$BASE/attachments?fileName={file name}&api-version=7.1" \
|
|
215
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/octet-stream" \
|
|
216
|
+
--data-binary @"{repo path}"
|
|
217
|
+
|
|
218
|
+
# 2. Link it to the work item. The body goes in a scratch file, because the run command
|
|
219
|
+
# in the comment usually has quotes that break inline JSON:
|
|
220
|
+
# [{"op":"test","path":"/rev","value":{rev from a fresh get}},
|
|
221
|
+
# {"op":"add","path":"/relations/-","value":{"rel":"AttachedFile","url":"{attachment url}",
|
|
222
|
+
# "attributes":{"comment":"{repo path} — {how to run}"}}}]
|
|
223
|
+
curl -sS -X PATCH "$BASE/workitems/{id}?api-version=7.1" \
|
|
224
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json-patch+json" \
|
|
225
|
+
--data-binary @"{scratch dir}/attach-patch.json"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Never print the token. If `az` has no session, swap the bearer header for `-u ":$AZURE_DEVOPS_PAT"`; if neither works, ask the user to run `! az login`. A failed attachment never blocks the PR, and it is never skipped quietly — the summary says `⚠️ NOT attached: {file} → AB#{id}` so it can be fixed before the first promotion.
|
|
229
|
+
|
|
192
230
|
**Reported, never enforced.** A script never blocks a promotion and never changes the gate check — it may already have been run, or be a reference copy. The person deploying decides; the commands only make sure nobody finds out afterwards.
|
|
193
231
|
|
|
194
232
|
#### Verifier Assignment
|