@we-scrum/cli 6.9.2 → 6.9.5
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 +17 -2
- package/dist/cli.js +117 -55
- package/package.json +5 -5
- package/src/cli.ts +2 -0
- package/src/commands/change-thread.ts +53 -0
- package/src/commands/index.ts +1 -0
- package/src/commands/task.ts +9 -6
- package/src/helpers/we-scrum.helper.ts +103 -22
- package/templates/develop-story-skill.hbs +2 -0
- package/templates/get-next-task.hbs +24 -11
- package/templates/review-analysis-skill.hbs +38 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@we-scrum/cli",
|
|
3
|
-
"version": "6.9.
|
|
3
|
+
"version": "6.9.5",
|
|
4
4
|
"description": "Cli tool for we-scrum application",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"@types/node": "^24.13.3",
|
|
38
38
|
"tsup": "^8.5.1",
|
|
39
39
|
"typescript": "^6.0.3",
|
|
40
|
+
"@my-devkit/cli": "2.1.0",
|
|
41
|
+
"@we-scrum/enums": "1.0.0",
|
|
42
|
+
"@my-devkit/core": "1.0.0",
|
|
40
43
|
"@we-scrum/commands": "1.0.0",
|
|
41
44
|
"@we-scrum/models": "1.0.0",
|
|
42
|
-
"@
|
|
43
|
-
"@we-scrum/utils": "1.0.0",
|
|
44
|
-
"@we-scrum/enums": "1.0.0",
|
|
45
|
-
"@my-devkit/cli": "2.1.0"
|
|
45
|
+
"@we-scrum/utils": "1.0.0"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"start": "node dist/cli.js",
|
package/src/cli.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Command } from 'commander';
|
|
|
4
4
|
import { version } from '../package.json';
|
|
5
5
|
import {
|
|
6
6
|
registerAuthCommands,
|
|
7
|
+
registerChangeThreadCommands,
|
|
7
8
|
registerIterationCommands,
|
|
8
9
|
registerPolicyCommands,
|
|
9
10
|
registerProjectCommands,
|
|
@@ -23,5 +24,6 @@ registerStoryCommands(program);
|
|
|
23
24
|
registerTaskCommands(program);
|
|
24
25
|
registerPolicyCommands(program);
|
|
25
26
|
registerIterationCommands(program);
|
|
27
|
+
registerChangeThreadCommands(program);
|
|
26
28
|
|
|
27
29
|
program.parse(process.argv);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { WeScrumHelper } from '@helpers';
|
|
2
|
+
import { Logger } from '@my-devkit/core';
|
|
3
|
+
import { runProjectCommand } from '@utils';
|
|
4
|
+
import { ChangeThreadStatus } from '@we-scrum/enums';
|
|
5
|
+
import { Command, Option } from 'commander';
|
|
6
|
+
|
|
7
|
+
export function registerChangeThreadCommands(program: Command): void {
|
|
8
|
+
program
|
|
9
|
+
.command('get-change-threads')
|
|
10
|
+
.description('Get all the change threads (any status) for a change')
|
|
11
|
+
.requiredOption('-c, --changeId <id>', 'Unique identifier of the change')
|
|
12
|
+
.action(
|
|
13
|
+
runProjectCommand<{ changeId: string }>(async (options) => {
|
|
14
|
+
return WeScrumHelper.getChangeThreads(options.changeId);
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command('create-change-thread')
|
|
20
|
+
.description('Open a new change thread with a first comment')
|
|
21
|
+
.requiredOption('-c, --changeId <id>', 'Unique identifier of the change')
|
|
22
|
+
.requiredOption('-m, --comment <comment>', 'Comment body (markdown supported)')
|
|
23
|
+
.action(
|
|
24
|
+
runProjectCommand<{ changeId: string; comment: string }>(async (options) => {
|
|
25
|
+
const changeThreadId = await WeScrumHelper.createChangeThread(options.changeId, options.comment);
|
|
26
|
+
Logger.info(`Change thread "${changeThreadId}" created successfully.`);
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
program
|
|
31
|
+
.command('update-change-thread-status')
|
|
32
|
+
.description('Update the status of a change thread')
|
|
33
|
+
.requiredOption('-t, --changeThreadId <id>', 'Unique identifier of the change thread')
|
|
34
|
+
.addOption(new Option('-s, --status <status>', 'New status').choices(ChangeThreadStatus.helper.members).makeOptionMandatory())
|
|
35
|
+
.action(
|
|
36
|
+
runProjectCommand<{ changeThreadId: string; status: ChangeThreadStatus }>(async (options) => {
|
|
37
|
+
await WeScrumHelper.updateChangeThreadStatus(options.changeThreadId, options.status);
|
|
38
|
+
Logger.info('Change thread status updated successfully.');
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command('add-comment-to-change-thread')
|
|
44
|
+
.description('Reply to an existing change thread')
|
|
45
|
+
.requiredOption('-t, --changeThreadId <id>', 'Unique identifier of the change thread')
|
|
46
|
+
.requiredOption('-m, --comment <comment>', 'Comment body (markdown supported)')
|
|
47
|
+
.action(
|
|
48
|
+
runProjectCommand<{ changeThreadId: string; comment: string }>(async (options) => {
|
|
49
|
+
await WeScrumHelper.addCommentToChangeThread(options.changeThreadId, options.comment);
|
|
50
|
+
Logger.info('Comment added successfully.');
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
}
|
package/src/commands/index.ts
CHANGED
package/src/commands/task.ts
CHANGED
|
@@ -8,10 +8,10 @@ export function registerTaskCommands(program: Command): void {
|
|
|
8
8
|
.command('start-task')
|
|
9
9
|
.description('Mark a specific task as in progress')
|
|
10
10
|
.requiredOption('-i, --identificationNumber <id>', 'Story identification number')
|
|
11
|
-
.requiredOption('-
|
|
11
|
+
.requiredOption('-c, --changeId <id>', 'Unique identifier of the change to act on')
|
|
12
12
|
.action(
|
|
13
|
-
runProjectCommand<{ identificationNumber: string;
|
|
14
|
-
await WeScrumHelper.startTask(options.identificationNumber, options.
|
|
13
|
+
runProjectCommand<{ identificationNumber: string; changeId: string }>(async (options) => {
|
|
14
|
+
await WeScrumHelper.startTask(options.identificationNumber, options.changeId);
|
|
15
15
|
Logger.info('Task started successfully. You can now make changes to the codebase.');
|
|
16
16
|
}),
|
|
17
17
|
);
|
|
@@ -20,11 +20,14 @@ export function registerTaskCommands(program: Command): void {
|
|
|
20
20
|
.command('complete-task')
|
|
21
21
|
.description('Mark the current in-progress task as done')
|
|
22
22
|
.requiredOption('-i, --identificationNumber <id>', 'Story identification number')
|
|
23
|
-
.requiredOption('-
|
|
23
|
+
.requiredOption('-c, --changeId <id>', 'Unique identifier of the change to act on')
|
|
24
24
|
.action(
|
|
25
|
-
runProjectCommand<{ identificationNumber: string;
|
|
26
|
-
await WeScrumHelper.completeTask(options.identificationNumber, options.
|
|
25
|
+
runProjectCommand<{ identificationNumber: string; changeId: string }>(async (options) => {
|
|
26
|
+
await WeScrumHelper.completeTask(options.identificationNumber, options.changeId);
|
|
27
27
|
Logger.info('Task completed successfully.');
|
|
28
|
+
Logger.info(
|
|
29
|
+
'Reminder: if the result deviates from the task description (including changes requested by the developer), or if the development guidelines were missing or incomplete, post the matching change thread(s) now with "we-scrum create-change-thread".',
|
|
30
|
+
);
|
|
28
31
|
}),
|
|
29
32
|
);
|
|
30
33
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { _sortBy, assert, Command, serialize, TypeHelper } from '@my-devkit/core';
|
|
1
|
+
import { _sortBy, assert, Command, ContributionOrigin, serialize, TypeHelper } from '@my-devkit/core';
|
|
2
2
|
import {
|
|
3
|
+
AddCommentToChangeThreadCommand,
|
|
4
|
+
CreateChangeThreadCommand,
|
|
3
5
|
MarkEnumerationChangeAsDoneCommand,
|
|
4
6
|
MarkObjectChangeAsDoneCommand,
|
|
5
7
|
MarkRelationChangeAsDoneCommand,
|
|
@@ -11,11 +13,13 @@ import {
|
|
|
11
13
|
TakeRelationChangeCommand,
|
|
12
14
|
TakeRouteChangeCommand,
|
|
13
15
|
TakeTaskCommand,
|
|
16
|
+
UpdateChangeThreadStatusCommand,
|
|
14
17
|
UpdateIterationCommand,
|
|
15
18
|
} from '@we-scrum/commands';
|
|
16
|
-
import { ContentType, ProgressStatus } from '@we-scrum/enums';
|
|
19
|
+
import { ChangeThreadStatus, ContentType, ProgressStatus } from '@we-scrum/enums';
|
|
17
20
|
import {
|
|
18
21
|
DevelopmentPolicyModel,
|
|
22
|
+
ProjectChangeThreadModel,
|
|
19
23
|
ProjectIterationModel,
|
|
20
24
|
ProjectStoryModel,
|
|
21
25
|
ProjectStorySectionModel,
|
|
@@ -33,6 +37,8 @@ import { FirebaseHelper } from './firebase-helper';
|
|
|
33
37
|
import { ProjectHelper } from './project-helper';
|
|
34
38
|
import { renderTemplate } from './template.helper';
|
|
35
39
|
|
|
40
|
+
const CHANGE_THREAD_STATUS_DISPLAY_ORDER = [ChangeThreadStatus.Open, ChangeThreadStatus.Resolved, ChangeThreadStatus.Archived];
|
|
41
|
+
|
|
36
42
|
export class WeScrumHelper {
|
|
37
43
|
private static readonly backendUrl = 'https://europe-west1-we-scrum-prod.cloudfunctions.net';
|
|
38
44
|
constructor(private userId: string) {}
|
|
@@ -63,6 +69,8 @@ export class WeScrumHelper {
|
|
|
63
69
|
});
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
this.appendThreadsHint(nextChange);
|
|
73
|
+
|
|
66
74
|
const analysisChange = StorySectionHelper.mapSingleChange(nextChange);
|
|
67
75
|
const isTodo = nextChange.progress.status === ProgressStatus.ToDo;
|
|
68
76
|
const developmentPolicy = await this.getChangeDevelopmentPolicy(nextChange);
|
|
@@ -74,7 +82,7 @@ export class WeScrumHelper {
|
|
|
74
82
|
unitTestsPath: developmentPolicy?.areUnitTestsMandatory ? unitTestsPath : null,
|
|
75
83
|
isTodo,
|
|
76
84
|
identificationNumber,
|
|
77
|
-
|
|
85
|
+
changeId: analysisChange.id,
|
|
78
86
|
});
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -85,11 +93,7 @@ export class WeScrumHelper {
|
|
|
85
93
|
|
|
86
94
|
const sections = await this.getStorySections(story.storyId);
|
|
87
95
|
|
|
88
|
-
|
|
89
|
-
.flatMap((s) => [...s.enumerationChanges, ...s.objectChanges, ...s.routeChanges, ...s.relationChanges, ...s.tasks])
|
|
90
|
-
.find((t) => t.progress?.status !== ProgressStatus.Done);
|
|
91
|
-
|
|
92
|
-
return task;
|
|
96
|
+
return this.getAllChanges(sections).find((t) => t.progress?.status !== ProgressStatus.Done);
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
public static async getChangeById(identificationNumber: string, changeId: string) {
|
|
@@ -98,23 +102,14 @@ export class WeScrumHelper {
|
|
|
98
102
|
|
|
99
103
|
const sections = await this.getStorySections(story.storyId);
|
|
100
104
|
|
|
101
|
-
const change =
|
|
102
|
-
.flatMap((s) => [...s.enumerationChanges, ...s.objectChanges, ...s.routeChanges, ...s.relationChanges, ...s.tasks])
|
|
103
|
-
.find((t) => {
|
|
104
|
-
if (t instanceof ProjectStorySectionModelEnumerationChange) return t.enumerationChangeId === changeId;
|
|
105
|
-
if (t instanceof ProjectStorySectionModelObjectChange) return t.objectChangeId === changeId;
|
|
106
|
-
if (t instanceof ProjectStorySectionModelRouteChange) return t.routeChangeId === changeId;
|
|
107
|
-
if (t instanceof ProjectStorySectionModelRelationChange) return t.relationChangeId === changeId;
|
|
108
|
-
if (t instanceof ProjectStorySectionModelTask) return t.taskId === changeId;
|
|
109
|
-
return false;
|
|
110
|
-
});
|
|
105
|
+
const change = this.getAllChanges(sections).find((t) => this.getChangeIdentifier(t) === changeId);
|
|
111
106
|
|
|
112
107
|
assert(!!change, `Change "${changeId}" not found in story "${identificationNumber}".`);
|
|
113
108
|
return change;
|
|
114
109
|
}
|
|
115
110
|
|
|
116
|
-
public static async startTask(identificationNumber: string,
|
|
117
|
-
const change = await this.getChangeById(identificationNumber,
|
|
111
|
+
public static async startTask(identificationNumber: string, changeId: string): Promise<void> {
|
|
112
|
+
const change = await this.getChangeById(identificationNumber, changeId);
|
|
118
113
|
|
|
119
114
|
assert(change.progress.status === ProgressStatus.ToDo, `Task is not in ToDo status (current status: ${change.progress.status}).`);
|
|
120
115
|
|
|
@@ -143,8 +138,8 @@ export class WeScrumHelper {
|
|
|
143
138
|
}
|
|
144
139
|
}
|
|
145
140
|
|
|
146
|
-
public static async completeTask(identificationNumber: string,
|
|
147
|
-
const change = await this.getChangeById(identificationNumber,
|
|
141
|
+
public static async completeTask(identificationNumber: string, changeId: string): Promise<void> {
|
|
142
|
+
const change = await this.getChangeById(identificationNumber, changeId);
|
|
148
143
|
|
|
149
144
|
assert(change.progress.status === ProgressStatus.Doing, `Task is not in progress (current status: ${change.progress.status}).`);
|
|
150
145
|
|
|
@@ -237,11 +232,96 @@ export class WeScrumHelper {
|
|
|
237
232
|
assert(!!story, `Story "${identificationNumber}" not found in the active project.`);
|
|
238
233
|
|
|
239
234
|
const sections = await this.getStorySections(story.storyId);
|
|
235
|
+
this.getAllChanges(sections).forEach((change) => this.appendThreadsHint(change));
|
|
236
|
+
|
|
240
237
|
const ast = StorySectionHelper.mapSectionsToAnalysisAst(sections);
|
|
241
238
|
|
|
242
239
|
return AnalysisDsl.stringify(ast);
|
|
243
240
|
}
|
|
244
241
|
|
|
242
|
+
public static async getChangeThreads(changeId: string): Promise<string> {
|
|
243
|
+
const threads = await FirebaseHelper.getCollection<ProjectChangeThreadModel>(`/projects/${this.projectId}/change-threads`, {
|
|
244
|
+
where: [['changeId', '==', changeId]],
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (threads.length === 0) {
|
|
248
|
+
return `No threads for change "${changeId}".`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return _sortBy(
|
|
252
|
+
threads,
|
|
253
|
+
(thread) => CHANGE_THREAD_STATUS_DISPLAY_ORDER.indexOf(thread.status),
|
|
254
|
+
(thread) => thread.comments[0]?.createdAt,
|
|
255
|
+
)
|
|
256
|
+
.map((thread) => this.formatChangeThread(thread))
|
|
257
|
+
.join('\n\n---\n\n');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public static async createChangeThread(changeId: string, comment: string): Promise<string> {
|
|
261
|
+
return this.post(
|
|
262
|
+
'project-management/create-change-thread',
|
|
263
|
+
TypeHelper.transform(CreateChangeThreadCommand, { changeId, commentBody: comment }),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public static async addCommentToChangeThread(changeThreadId: string, comment: string): Promise<void> {
|
|
268
|
+
await this.post(
|
|
269
|
+
'project-management/add-comment-to-change-thread',
|
|
270
|
+
TypeHelper.transform(AddCommentToChangeThreadCommand, { changeThreadId, commentBody: comment }),
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
public static async updateChangeThreadStatus(changeThreadId: string, status: ChangeThreadStatus): Promise<void> {
|
|
275
|
+
await this.post(
|
|
276
|
+
'project-management/update-change-thread-status',
|
|
277
|
+
TypeHelper.transform(UpdateChangeThreadStatusCommand, { changeThreadId, status }),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private static formatChangeThread(thread: ProjectChangeThreadModel): string {
|
|
282
|
+
const comments = thread.comments
|
|
283
|
+
.map((comment) => {
|
|
284
|
+
const author = comment.contributionOrigin === ContributionOrigin.Cli ? 'Claude Code' : comment.createdByUserFullName;
|
|
285
|
+
return `**${author}** (${comment.createdAt.toISOString()}):\n${comment.body}`;
|
|
286
|
+
})
|
|
287
|
+
.join('\n\n');
|
|
288
|
+
|
|
289
|
+
return `## Thread ${thread.changeThreadId} (${thread.status})\n\n${comments}`;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private static getAllChanges(sections: ProjectStorySectionModel[]): DevelopmentPolicyHelper.Change[] {
|
|
293
|
+
return _sortBy(sections, (s) => s.name).flatMap((s) => [
|
|
294
|
+
...s.enumerationChanges,
|
|
295
|
+
...s.objectChanges,
|
|
296
|
+
...s.routeChanges,
|
|
297
|
+
...s.relationChanges,
|
|
298
|
+
...s.tasks,
|
|
299
|
+
]);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private static getChangeIdentifier(change: DevelopmentPolicyHelper.Change): string {
|
|
303
|
+
if (change instanceof ProjectStorySectionModelEnumerationChange) return change.enumerationChangeId;
|
|
304
|
+
if (change instanceof ProjectStorySectionModelObjectChange) return change.objectChangeId;
|
|
305
|
+
if (change instanceof ProjectStorySectionModelRouteChange) return change.routeChangeId;
|
|
306
|
+
if (change instanceof ProjectStorySectionModelRelationChange) return change.relationChangeId;
|
|
307
|
+
return change.taskId;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private static appendThreadsHint(change: DevelopmentPolicyHelper.Change): void {
|
|
311
|
+
const synthesis = CHANGE_THREAD_STATUS_DISPLAY_ORDER.map((status) => ({
|
|
312
|
+
status,
|
|
313
|
+
count: change.threads.filter((t) => t.status === status).length,
|
|
314
|
+
}))
|
|
315
|
+
.filter(({ count }) => count > 0)
|
|
316
|
+
.map(({ status, count }) => `${count} ${status}`)
|
|
317
|
+
.join(' • ');
|
|
318
|
+
if (!synthesis) return;
|
|
319
|
+
|
|
320
|
+
const changeId = this.getChangeIdentifier(change);
|
|
321
|
+
const hint = `Threads: ${synthesis} — run \`we-scrum get-change-threads --changeId "${changeId}"\` to view them.`;
|
|
322
|
+
change.description = change.description ? `${change.description}\n\n${hint}` : hint;
|
|
323
|
+
}
|
|
324
|
+
|
|
245
325
|
private static async findStoryByIterationNumber(identificationNumber: string) {
|
|
246
326
|
return FirebaseHelper.findDocument<ProjectStoryModel>(`/projects/${this.projectId}/stories`, [
|
|
247
327
|
['identificationNumber', '==', identificationNumber],
|
|
@@ -287,6 +367,7 @@ export class WeScrumHelper {
|
|
|
287
367
|
headers: {
|
|
288
368
|
'Content-Type': 'application/json',
|
|
289
369
|
Authorization: `Bearer ${userIdToken}`,
|
|
370
|
+
'X-Contribution-Origin': ContributionOrigin.Cli,
|
|
290
371
|
},
|
|
291
372
|
signal: AbortSignal.timeout(60 * 1000),
|
|
292
373
|
});
|
|
@@ -56,3 +56,5 @@ Before committing any task, run ESLint on every modified file and fix all report
|
|
|
56
56
|
```bash
|
|
57
57
|
npx eslint <file1> <file2> ...
|
|
58
58
|
```
|
|
59
|
+
|
|
60
|
+
Every task ends with a mandatory pre-completion check (deviation thread and development guidelines feedback thread, see the task's "Next steps"). Adjustments requested by the developer during or after a task count as deviations. If a thread is needed, post it before running `we-scrum complete-task`; if you realize afterwards that one was missed, post it right away.
|
|
@@ -5,21 +5,34 @@
|
|
|
5
5
|
{{{taskDetails}}}
|
|
6
6
|
|
|
7
7
|
## Development guidelines
|
|
8
|
-
{{#if guidelinesPath}}Refer to `{{{guidelinesPath}}}` for coding conventions and project-specific instructions.{{else}}_No development guidelines file configured for this task._{{/if}}
|
|
8
|
+
{{#if guidelinesPath}}Refer to `{{{guidelinesPath}}}` for coding conventions and project-specific instructions.{{else}}_No development guidelines file configured for this task._ **You must therefore open a development guidelines feedback thread before completing the task (see the pre-completion check below).**{{/if}}
|
|
9
9
|
|
|
10
10
|
## Unit tests
|
|
11
11
|
{{#if unitTestsPath}}Unit tests are required. Refer to `{{{unitTestsPath}}}` for test writing guidelines.{{else}}No unit tests required.{{/if}}
|
|
12
12
|
|
|
13
13
|
## Next steps
|
|
14
14
|
{{#if isTodo}}
|
|
15
|
-
1. Run `we-scrum start-task --identificationNumber "{{identificationNumber}}" --
|
|
16
|
-
2. Implement the task described above.
|
|
17
|
-
3. Commit your changes.
|
|
18
|
-
4. Run `we-scrum complete-task --identificationNumber "{{identificationNumber}}" --taskId "{{taskId}}"` when done.
|
|
19
|
-
5. Call `get_next_task` to continue with the next task.
|
|
20
|
-
{{else}}
|
|
21
|
-
1. Implement the task described above.
|
|
22
|
-
2. Commit your changes.
|
|
23
|
-
3. Run `we-scrum complete-task --identificationNumber "{{identificationNumber}}" --taskId "{{taskId}}"` when done.
|
|
24
|
-
4. Call `get_next_task` to continue with the next task.
|
|
15
|
+
1. Run `we-scrum start-task --identificationNumber "{{identificationNumber}}" --changeId "{{changeId}}"` before making any changes.
|
|
25
16
|
{{/if}}
|
|
17
|
+
1. If the task above mentions threads, run `we-scrum get-change-threads --changeId "{{changeId}}"` before implementing. It returns **all** threads of the change (Open, Resolved, Archived) so you have the complete history. If an **open** thread requests an adjustment to this task, make it part of the implementation below, then resolve the thread once done:
|
|
18
|
+
```bash
|
|
19
|
+
we-scrum add-comment-to-change-thread --changeThreadId <changeThreadId> --comment "<markdown explaining what was done>"
|
|
20
|
+
we-scrum update-change-thread-status --changeThreadId <changeThreadId> --status Resolved
|
|
21
|
+
```
|
|
22
|
+
Leave a thread open (with a reply explaining why) if it does not call for a change to this task, or if you disagree with the request. Resolved and Archived threads are history only: do not act on them.
|
|
23
|
+
1. Implement the task described above.
|
|
24
|
+
1. Commit your changes.
|
|
25
|
+
1. **Pre-completion check — mandatory, answer both questions explicitly before running `complete-task`.** Threads posted after completion are late, so do this now:
|
|
26
|
+
- **Deviations:** does the final result differ in any way from what this task describes (different naming, different location, a sub-step skipped or altered, a workaround for a blocker, a mistake you had to correct mid-way)? Changes made at the developer's request during the session (after feedback, redesigns, extra options or components added) count as deviations too. If yes, first run `we-scrum get-change-threads` and check the existing threads. If a thread already covers the deviation (whatever its status), reply to it with `we-scrum add-comment-to-change-thread` instead of creating a duplicate; otherwise post **one** thread explaining what differs and why:
|
|
27
|
+
```bash
|
|
28
|
+
we-scrum create-change-thread --changeId "{{changeId}}" --comment "<markdown explaining the deviation>"
|
|
29
|
+
```
|
|
30
|
+
Group related deviations into a single thread; use a few separate threads only for several unrelated deviations, and never one thread per minor deviation. Skip only if the task was implemented exactly as described.
|
|
31
|
+
- **Guidelines:** was the development guidelines section above "No development guidelines file configured", or were the development / unit tests guidelines incomplete, outdated, or missing for this kind of task (you had to spend extra effort figuring out conventions)? If yes, check the existing threads first (same rule: reply to an existing guidelines thread rather than creating a duplicate), otherwise open a thread with a concrete suggested addition or correction:
|
|
32
|
+
```bash
|
|
33
|
+
we-scrum create-change-thread --changeId "{{changeId}}" --comment "<markdown: what was missing/outdated, and a suggested fix>"
|
|
34
|
+
```
|
|
35
|
+
Use at most one thread for all development guidelines feedback and, separately, at most one for all unit tests guidelines feedback; do not mix them together, and do not mix guideline feedback into the deviation thread. Skip whichever guideline was accurate and sufficient.
|
|
36
|
+
- If the developer asks for adjustments **after** the task is completed, add them to the deviation thread (reply with `we-scrum add-comment-to-change-thread`, or open one if none exists).
|
|
37
|
+
1. Run `we-scrum complete-task --identificationNumber "{{identificationNumber}}" --changeId "{{changeId}}"` when done.
|
|
38
|
+
1. Call `get_next_task` to continue with the next task.
|
|
@@ -34,7 +34,29 @@ we-scrum get-story-analysis --identificationNumber <identificationNumber>
|
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
37
|
-
## Step 2 — Review
|
|
37
|
+
## Step 2 — Review existing threads
|
|
38
|
+
|
|
39
|
+
The analysis DSL may annotate a change with a note such as _"Threads: 3 Open • 2 Resolved — run `we-scrum get-change-threads --changeId <id>` to view them."_ This means earlier review comments exist on that change (statuses with no thread are omitted).
|
|
40
|
+
|
|
41
|
+
For every change annotated this way:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
we-scrum get-change-threads --changeId <changeId>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The command returns **all** threads of the change (Open, Resolved, Archived), so you have the complete history. Resolved and Archived threads are history only: read them to avoid repeating a concern that was already handled, but do not act on them.
|
|
48
|
+
|
|
49
|
+
For each **open** thread returned:
|
|
50
|
+
- If the current state of the analysis addresses the concern raised in the thread, resolve it and explain why:
|
|
51
|
+
```bash
|
|
52
|
+
we-scrum add-comment-to-change-thread --changeThreadId <changeThreadId> --comment "<markdown explaining why this is resolved>"
|
|
53
|
+
we-scrum update-change-thread-status --changeThreadId <changeThreadId> --status Resolved
|
|
54
|
+
```
|
|
55
|
+
- If the concern still stands, leave the thread open. Only add a follow-up comment via `add-comment-to-change-thread` if you have new information to add — do not just repeat the original comment.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Step 3 — Review the analysis
|
|
38
60
|
|
|
39
61
|
{{> analysis-dsl-legend}}
|
|
40
62
|
|
|
@@ -60,4 +82,18 @@ Review the story description and analysis DSL together. Check for:
|
|
|
60
82
|
- Any change that adds, removes, or renames a field on an Aggregate or a Model must be accompanied by a migration script task in the analysis
|
|
61
83
|
- If such a structural change is present but no corresponding migration task exists, raise it as a blocking issue — shipping without a migration will corrupt existing data or break reads
|
|
62
84
|
|
|
63
|
-
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Step 4 — Post findings as change threads
|
|
88
|
+
|
|
89
|
+
Do not just report new findings in the chat — open a change thread on the change each finding concerns:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
we-scrum create-change-thread --changeId <changeId> --comment "<markdown-formatted finding>"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- The comment body supports Markdown (headings, lists, bold, code spans) — it renders as such in the we-scrum UI.
|
|
96
|
+
- Group every finding for a given change into a single thread/comment rather than opening one thread per bullet point.
|
|
97
|
+
- Skip changes that already have a thread covering the same concern, whatever its status (see Step 2) — reply on the existing thread instead of duplicating it.
|
|
98
|
+
|
|
99
|
+
Once threads are created or resolved, give the user a short summary in the chat: what was found, what was posted or resolved, and why — do not skip this summary just because the details live in we-scrum.
|