github-issue-tower-defence-management 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +0 -2
- package/bin/adapter/entry-points/cli/index.js +0 -5
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/cli/projectConfig.js +0 -3
- package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +1 -3
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/domain/usecases/CheckIssueReviewReadinessUseCase.js +4 -2
- package/bin/domain/usecases/CheckIssueReviewReadinessUseCase.js.map +1 -1
- package/bin/domain/usecases/HandleScheduledEventUseCase.js +1 -6
- package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
- package/bin/domain/usecases/IssueRejectionEvaluator.js +0 -23
- package/bin/domain/usecases/IssueRejectionEvaluator.js.map +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +39 -69
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +3 -2
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +0 -43
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/autoStatusCheckComments.js +1 -11
- package/bin/domain/usecases/autoStatusCheckComments.js.map +1 -1
- package/bin/domain/usecases/extractNextStepAgentFromComments.js +14 -0
- package/bin/domain/usecases/extractNextStepAgentFromComments.js.map +1 -0
- package/bin/domain/usecases/triagerAgentName.js +7 -0
- package/bin/domain/usecases/triagerAgentName.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +0 -2
- package/src/adapter/entry-points/cli/index.ts +0 -9
- package/src/adapter/entry-points/cli/projectConfig.ts +0 -8
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -7
- package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +26 -4
- package/src/domain/usecases/CheckIssueReviewReadinessUseCase.ts +5 -3
- package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +0 -28
- package/src/domain/usecases/HandleScheduledEventUseCase.ts +0 -6
- package/src/domain/usecases/IssueRejectionEvaluator.test.ts +0 -119
- package/src/domain/usecases/IssueRejectionEvaluator.ts +0 -21
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +185 -327
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +68 -138
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +4 -4
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +11 -5
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +0 -313
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +1 -102
- package/src/domain/usecases/autoStatusCheckComments.ts +0 -17
- package/src/domain/usecases/extractNextStepAgentFromComments.ts +13 -0
- package/src/domain/usecases/isHumanComment.test.ts +0 -13
- package/src/domain/usecases/triagerAgentName.ts +4 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/cli/projectConfig.d.ts +0 -1
- package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/domain/usecases/CheckIssueReviewReadinessUseCase.d.ts.map +1 -1
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +1 -3
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts +0 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts.map +1 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +0 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts +0 -1
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/autoStatusCheckComments.d.ts +0 -4
- package/types/domain/usecases/autoStatusCheckComments.d.ts.map +1 -1
- package/types/domain/usecases/extractNextStepAgentFromComments.d.ts +5 -0
- package/types/domain/usecases/extractNextStepAgentFromComments.d.ts.map +1 -0
- package/types/domain/usecases/triagerAgentName.d.ts +3 -0
- package/types/domain/usecases/triagerAgentName.d.ts.map +1 -0
- package/src/domain/usecases/TriagerApprovalDispatchUseCase.test.ts +0 -1051
- package/src/domain/usecases/TriagerApprovalDispatchUseCase.ts +0 -274
- package/src/domain/usecases/awaitingOwnerApprovalMessage.ts +0 -5
- package/src/domain/usecases/isPullRequestDeclaredUnnecessary.test.ts +0 -162
- package/src/domain/usecases/isPullRequestDeclaredUnnecessary.ts +0 -29
- package/src/domain/usecases/isWaitingForOwnerApproval.test.ts +0 -91
- package/src/domain/usecases/isWaitingForOwnerApproval.ts +0 -17
- package/src/domain/usecases/returnedToAwaitingWorkspaceMessage.ts +0 -5
|
@@ -15,16 +15,7 @@ import {
|
|
|
15
15
|
} from './IssueRejectionEvaluator';
|
|
16
16
|
import { ChangeTargetPullRequestApprover } from './ChangeTargetPullRequestApprover';
|
|
17
17
|
import { resolveLabelsNotRequiringPullRequest } from './resolveLabelsNotRequiringPullRequest';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
RETURNED_TO_AWAITING_WORKSPACE_MESSAGE,
|
|
21
|
-
RETURNED_TO_AWAITING_WORKSPACE_MESSAGE_HEAD,
|
|
22
|
-
} from './returnedToAwaitingWorkspaceMessage';
|
|
23
|
-
import { isWaitingForOwnerApproval } from './isWaitingForOwnerApproval';
|
|
24
|
-
import {
|
|
25
|
-
AWAITING_OWNER_APPROVAL_MESSAGE,
|
|
26
|
-
AWAITING_OWNER_APPROVAL_MESSAGE_HEAD,
|
|
27
|
-
} from './awaitingOwnerApprovalMessage';
|
|
18
|
+
import { isTriagerAgentName } from './triagerAgentName';
|
|
28
19
|
import {
|
|
29
20
|
ConsoleListItem,
|
|
30
21
|
ConsoleTabName,
|
|
@@ -127,7 +118,6 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
127
118
|
manager?: string | null;
|
|
128
119
|
developerAgentName?: string | null;
|
|
129
120
|
deferPreparation?: boolean | null;
|
|
130
|
-
ownerApprovalTimeoutCycles?: number | null;
|
|
131
121
|
}): Promise<void> => {
|
|
132
122
|
const project = await this.projectRepository.getByUrl(params.projectUrl);
|
|
133
123
|
|
|
@@ -257,110 +247,12 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
257
247
|
const nextStepAgent = lastAgentReport
|
|
258
248
|
? extractNextStepAgent(lastAgentReport.content)
|
|
259
249
|
: null;
|
|
260
|
-
if (nextStepAgent !== null) {
|
|
261
|
-
if (
|
|
262
|
-
params.agents &&
|
|
263
|
-
params.agents.length > 0 &&
|
|
264
|
-
!params.agents.includes(nextStepAgent)
|
|
265
|
-
) {
|
|
266
|
-
issue.status = FAILED_PREPARATION_STATUS_NAME;
|
|
267
|
-
await this.issueRepository.update(issue, project);
|
|
268
|
-
await this.issueRepository.updateStatus(
|
|
269
|
-
project,
|
|
270
|
-
issue,
|
|
271
|
-
failedPreparationStatusOption.id,
|
|
272
|
-
);
|
|
273
|
-
await this.patchConsoleTab(issue);
|
|
274
|
-
await this.issueCommentRepository.createComment(
|
|
275
|
-
issue,
|
|
276
|
-
`nextStepAgent '${nextStepAgent}' is not in the configured agents list. Update the configuration to include it.`,
|
|
277
|
-
);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
const repetition = resolveNextStepAgentDispatchRepetition({
|
|
281
|
-
agentFieldValue: issue.agent,
|
|
282
|
-
nextStepAgent,
|
|
283
|
-
comments,
|
|
284
|
-
isTrustedAuthor,
|
|
285
|
-
thresholdForAutoReject: params.thresholdForAutoReject,
|
|
286
|
-
thresholdForDispatchLoop:
|
|
287
|
-
params.thresholdForDispatchLoop ??
|
|
288
|
-
DEFAULT_THRESHOLD_FOR_DISPATCH_LOOP,
|
|
289
|
-
});
|
|
290
|
-
if (repetition.type === 'escalateToFailedPreparation') {
|
|
291
|
-
issue.status = FAILED_PREPARATION_STATUS_NAME;
|
|
292
|
-
await this.issueRepository.update(issue, project);
|
|
293
|
-
await this.issueRepository.updateStatus(
|
|
294
|
-
project,
|
|
295
|
-
issue,
|
|
296
|
-
failedPreparationStatusOption.id,
|
|
297
|
-
);
|
|
298
|
-
await this.patchConsoleTab(issue);
|
|
299
|
-
await this.issueCommentRepository.createComment(
|
|
300
|
-
issue,
|
|
301
|
-
repetition.comment,
|
|
302
|
-
);
|
|
303
|
-
await this.sendWorkflowBlockerNotification(
|
|
304
|
-
params.issueUrl,
|
|
305
|
-
params.workflowBlockerResolvedWebhookUrl,
|
|
306
|
-
project,
|
|
307
|
-
);
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const agentOptionId = await this.ensureAgentOptionAndGetId(
|
|
311
|
-
project,
|
|
312
|
-
nextStepAgent,
|
|
313
|
-
);
|
|
314
|
-
if (agentOptionId) {
|
|
315
|
-
await this.issueRepository.setIssueAgentField(
|
|
316
|
-
params.issueUrl,
|
|
317
|
-
project,
|
|
318
|
-
agentOptionId,
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
issue.status = AWAITING_WORKSPACE_STATUS_NAME;
|
|
322
|
-
await this.issueRepository.update(issue, project);
|
|
323
|
-
await this.issueRepository.updateStatus(
|
|
324
|
-
project,
|
|
325
|
-
issue,
|
|
326
|
-
awaitingWorkspaceStatusOption.id,
|
|
327
|
-
);
|
|
328
|
-
await this.patchConsoleTab(issue);
|
|
329
|
-
if (repetition.type === 'dispatchAgain') {
|
|
330
|
-
await this.issueCommentRepository.createComment(
|
|
331
|
-
issue,
|
|
332
|
-
repetition.comment,
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
250
|
if (
|
|
339
|
-
|
|
340
|
-
|
|
251
|
+
nextStepAgent !== null &&
|
|
252
|
+
params.agents &&
|
|
253
|
+
params.agents.length > 0 &&
|
|
254
|
+
!params.agents.includes(nextStepAgent)
|
|
341
255
|
) {
|
|
342
|
-
const ownerApprovalTimeoutCycles =
|
|
343
|
-
params.ownerApprovalTimeoutCycles ?? 12;
|
|
344
|
-
const awaitingOwnerApprovalCount = comments.filter(
|
|
345
|
-
(comment) =>
|
|
346
|
-
isTrustedAuthor(comment.author) &&
|
|
347
|
-
comment.content.startsWith(AWAITING_OWNER_APPROVAL_MESSAGE_HEAD),
|
|
348
|
-
).length;
|
|
349
|
-
if (awaitingOwnerApprovalCount < ownerApprovalTimeoutCycles) {
|
|
350
|
-
issue.status = AWAITING_QUALITY_CHECK_STATUS_NAME;
|
|
351
|
-
await this.issueRepository.update(issue, project);
|
|
352
|
-
await this.issueRepository.updateStatus(
|
|
353
|
-
project,
|
|
354
|
-
issue,
|
|
355
|
-
awaitingQualityCheckStatusOption.id,
|
|
356
|
-
);
|
|
357
|
-
await this.patchConsoleTab(issue);
|
|
358
|
-
await this.issueCommentRepository.createComment(
|
|
359
|
-
issue,
|
|
360
|
-
AWAITING_OWNER_APPROVAL_MESSAGE,
|
|
361
|
-
);
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
256
|
issue.status = FAILED_PREPARATION_STATUS_NAME;
|
|
365
257
|
await this.issueRepository.update(issue, project);
|
|
366
258
|
await this.issueRepository.updateStatus(
|
|
@@ -371,12 +263,7 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
371
263
|
await this.patchConsoleTab(issue);
|
|
372
264
|
await this.issueCommentRepository.createComment(
|
|
373
265
|
issue,
|
|
374
|
-
`
|
|
375
|
-
);
|
|
376
|
-
await this.sendWorkflowBlockerNotification(
|
|
377
|
-
params.issueUrl,
|
|
378
|
-
params.workflowBlockerResolvedWebhookUrl,
|
|
379
|
-
project,
|
|
266
|
+
`nextStepAgent '${nextStepAgent}' is not in the configured agents list. Update the configuration to include it.`,
|
|
380
267
|
);
|
|
381
268
|
return;
|
|
382
269
|
}
|
|
@@ -424,6 +311,7 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
424
311
|
comments,
|
|
425
312
|
isTrustedAuthor,
|
|
426
313
|
resolveLabelsNotRequiringPullRequest(params),
|
|
314
|
+
nextStepAgent,
|
|
427
315
|
params.developerAgentName,
|
|
428
316
|
);
|
|
429
317
|
|
|
@@ -475,17 +363,48 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
475
363
|
return;
|
|
476
364
|
}
|
|
477
365
|
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
366
|
+
if (nextStepAgent !== null) {
|
|
367
|
+
const repetition = resolveNextStepAgentDispatchRepetition({
|
|
368
|
+
agentFieldValue: issue.agent,
|
|
369
|
+
nextStepAgent,
|
|
370
|
+
comments,
|
|
371
|
+
isTrustedAuthor,
|
|
372
|
+
thresholdForAutoReject: params.thresholdForAutoReject,
|
|
373
|
+
thresholdForDispatchLoop:
|
|
374
|
+
params.thresholdForDispatchLoop ??
|
|
375
|
+
DEFAULT_THRESHOLD_FOR_DISPATCH_LOOP,
|
|
376
|
+
});
|
|
377
|
+
if (repetition.type === 'escalateToFailedPreparation') {
|
|
378
|
+
issue.status = FAILED_PREPARATION_STATUS_NAME;
|
|
379
|
+
await this.issueRepository.update(issue, project);
|
|
380
|
+
await this.issueRepository.updateStatus(
|
|
381
|
+
project,
|
|
382
|
+
issue,
|
|
383
|
+
failedPreparationStatusOption.id,
|
|
384
|
+
);
|
|
385
|
+
await this.patchConsoleTab(issue);
|
|
386
|
+
await this.issueCommentRepository.createComment(
|
|
387
|
+
issue,
|
|
388
|
+
repetition.comment,
|
|
389
|
+
);
|
|
390
|
+
await this.sendWorkflowBlockerNotification(
|
|
391
|
+
params.issueUrl,
|
|
392
|
+
params.workflowBlockerResolvedWebhookUrl,
|
|
393
|
+
project,
|
|
394
|
+
);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const agentOptionId = await this.ensureAgentOptionAndGetId(
|
|
398
|
+
project,
|
|
399
|
+
nextStepAgent,
|
|
400
|
+
);
|
|
401
|
+
if (agentOptionId) {
|
|
402
|
+
await this.issueRepository.setIssueAgentField(
|
|
403
|
+
params.issueUrl,
|
|
404
|
+
project,
|
|
405
|
+
agentOptionId,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
489
408
|
issue.status = AWAITING_WORKSPACE_STATUS_NAME;
|
|
490
409
|
await this.issueRepository.update(issue, project);
|
|
491
410
|
await this.issueRepository.updateStatus(
|
|
@@ -494,10 +413,23 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
494
413
|
awaitingWorkspaceStatusOption.id,
|
|
495
414
|
);
|
|
496
415
|
await this.patchConsoleTab(issue);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
416
|
+
if (rejections.length > 0) {
|
|
417
|
+
await this.setDependedIssueUrlForAllOpenPRs(
|
|
418
|
+
issue,
|
|
419
|
+
params.issueUrl,
|
|
420
|
+
project,
|
|
421
|
+
);
|
|
422
|
+
await this.issueCommentRepository.createComment(
|
|
423
|
+
issue,
|
|
424
|
+
rejectionStatusMessage,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
if (repetition.type === 'dispatchAgain') {
|
|
428
|
+
await this.issueCommentRepository.createComment(
|
|
429
|
+
issue,
|
|
430
|
+
repetition.comment,
|
|
431
|
+
);
|
|
432
|
+
}
|
|
501
433
|
return;
|
|
502
434
|
}
|
|
503
435
|
|
|
@@ -657,6 +589,7 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
657
589
|
comments: { author: string; content: string }[],
|
|
658
590
|
isTrustedAuthor: (author: string) => boolean,
|
|
659
591
|
labelsNotRequiringPullRequest: string[],
|
|
592
|
+
nextStepAgent: string | null,
|
|
660
593
|
developerAgentName?: string | null,
|
|
661
594
|
): Promise<{
|
|
662
595
|
rejections: { type: RejectedReasonType; detail: string }[];
|
|
@@ -687,10 +620,7 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
687
620
|
labelsNotRequiringPullRequest,
|
|
688
621
|
{ developerAgentName },
|
|
689
622
|
);
|
|
690
|
-
const requiredPrRejections =
|
|
691
|
-
comments,
|
|
692
|
-
isTrustedAuthor,
|
|
693
|
-
)
|
|
623
|
+
const requiredPrRejections = isTriagerAgentName(nextStepAgent)
|
|
694
624
|
? prRejections.filter(
|
|
695
625
|
(rejection) => rejection.type !== 'PULL_REQUEST_NOT_FOUND',
|
|
696
626
|
)
|
|
@@ -388,7 +388,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
388
388
|
);
|
|
389
389
|
});
|
|
390
390
|
|
|
391
|
-
it('should not revert an issue whose last agent report
|
|
391
|
+
it('should not revert an issue whose last agent report designates the triager', async () => {
|
|
392
392
|
const issue = createMockIssue({
|
|
393
393
|
status: 'Awaiting Quality Check',
|
|
394
394
|
});
|
|
@@ -401,7 +401,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
401
401
|
{
|
|
402
402
|
author: 'owner',
|
|
403
403
|
content:
|
|
404
|
-
'From: :robot: Agent report\n```json\n{"
|
|
404
|
+
'From: :robot: Agent report\n```json\n{"nextStepAgent": "triager"}\n```',
|
|
405
405
|
createdAt: new Date(),
|
|
406
406
|
},
|
|
407
407
|
]);
|
|
@@ -416,7 +416,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
416
416
|
expect(mockIssueCommentRepository.createComment).not.toHaveBeenCalled();
|
|
417
417
|
});
|
|
418
418
|
|
|
419
|
-
it('should still revert an issue whose linked PR is conflicted even when the last agent report
|
|
419
|
+
it('should still revert an issue whose linked PR is conflicted even when the last agent report designates the triager', async () => {
|
|
420
420
|
const issue = createMockIssue({
|
|
421
421
|
status: 'Awaiting Quality Check',
|
|
422
422
|
});
|
|
@@ -427,7 +427,7 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
|
|
|
427
427
|
{
|
|
428
428
|
author: 'owner',
|
|
429
429
|
content:
|
|
430
|
-
'From: :robot: Agent report\n```json\n{"
|
|
430
|
+
'From: :robot: Agent report\n```json\n{"nextStepAgent": "triager"}\n```',
|
|
431
431
|
createdAt: new Date(),
|
|
432
432
|
},
|
|
433
433
|
]);
|
|
@@ -5,7 +5,8 @@ import { IssueCommentRepository } from './adapter-interfaces/IssueCommentReposit
|
|
|
5
5
|
import { IssueRejectionEvaluator } from './IssueRejectionEvaluator';
|
|
6
6
|
import { ChangeTargetPullRequestApprover } from './ChangeTargetPullRequestApprover';
|
|
7
7
|
import { resolveLabelsNotRequiringPullRequest } from './resolveLabelsNotRequiringPullRequest';
|
|
8
|
-
import {
|
|
8
|
+
import { extractNextStepAgentFromComments } from './extractNextStepAgentFromComments';
|
|
9
|
+
import { isTriagerAgentName } from './triagerAgentName';
|
|
9
10
|
import { isAuthorAuthorizedForAutoStatusCheck } from './isAuthorAuthorizedForAutoStatusCheck';
|
|
10
11
|
import {
|
|
11
12
|
AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
@@ -132,10 +133,15 @@ export class RevertNotReadyReviewQueueIssueUseCase {
|
|
|
132
133
|
if (
|
|
133
134
|
rejections.length === 1 &&
|
|
134
135
|
rejections[0].type === 'PULL_REQUEST_NOT_FOUND' &&
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
isTriagerAgentName(
|
|
137
|
+
extractNextStepAgentFromComments(
|
|
138
|
+
await this.issueCommentRepository.getCommentsFromIssue(issue),
|
|
139
|
+
(author) =>
|
|
140
|
+
isAuthorAuthorizedForAutoStatusCheck(
|
|
141
|
+
author,
|
|
142
|
+
allowedIssueAuthors,
|
|
143
|
+
),
|
|
144
|
+
),
|
|
139
145
|
)
|
|
140
146
|
) {
|
|
141
147
|
continue;
|
|
@@ -597,226 +597,6 @@ describe('RevertOrphanedPreparationUseCase', () => {
|
|
|
597
597
|
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('1');
|
|
598
598
|
});
|
|
599
599
|
|
|
600
|
-
it('should return an orphaned issue to Awaiting Workspace without a rejection when the last report first declares that no pull request is required', async () => {
|
|
601
|
-
const stuckIssue = createMockIssue({
|
|
602
|
-
url: 'https://github.com/user/repo/issues/10',
|
|
603
|
-
status: 'Preparation',
|
|
604
|
-
});
|
|
605
|
-
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
606
|
-
project: mockProject,
|
|
607
|
-
issues: [stuckIssue],
|
|
608
|
-
cacheUsed: false,
|
|
609
|
-
});
|
|
610
|
-
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
611
|
-
stdout: '',
|
|
612
|
-
stderr: '',
|
|
613
|
-
exitCode: 1,
|
|
614
|
-
});
|
|
615
|
-
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
616
|
-
{
|
|
617
|
-
author: 'agent-bot',
|
|
618
|
-
content:
|
|
619
|
-
'From: :robot: agent report\n\n```json\n{ "pullRequestRequired": false, "nextStep": null }\n```\n',
|
|
620
|
-
createdAt: new Date(),
|
|
621
|
-
},
|
|
622
|
-
]);
|
|
623
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
624
|
-
|
|
625
|
-
await useCase.run({
|
|
626
|
-
projectUrl: 'https://github.com/user/repo',
|
|
627
|
-
preparationProcessCheckCommand: 'pgrep -fa "claude-agent.*{URL}"',
|
|
628
|
-
thresholdForAutoReject: 3,
|
|
629
|
-
allowedIssueAuthors: ['agent-bot'],
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
expect(mockIssueRepository.findRelatedOpenPRs.mock.calls).toHaveLength(0);
|
|
633
|
-
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
634
|
-
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('1');
|
|
635
|
-
expect(mockIssueCommentRepository.createComment.mock.calls).toHaveLength(1);
|
|
636
|
-
expect(mockIssueCommentRepository.createComment.mock.calls[0][1]).toContain(
|
|
637
|
-
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE',
|
|
638
|
-
);
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
it('should advance an orphaned issue to Awaiting Quality Check when it was already returned to the workspace once for the same declaration', async () => {
|
|
642
|
-
const stuckIssue = createMockIssue({
|
|
643
|
-
url: 'https://github.com/user/repo/issues/10',
|
|
644
|
-
status: 'Preparation',
|
|
645
|
-
});
|
|
646
|
-
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
647
|
-
project: mockProject,
|
|
648
|
-
issues: [stuckIssue],
|
|
649
|
-
cacheUsed: false,
|
|
650
|
-
});
|
|
651
|
-
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
652
|
-
stdout: '',
|
|
653
|
-
stderr: '',
|
|
654
|
-
exitCode: 1,
|
|
655
|
-
});
|
|
656
|
-
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
657
|
-
{
|
|
658
|
-
author: 'agent-bot',
|
|
659
|
-
content:
|
|
660
|
-
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE\nThe last report declared that this task needs no pull request.',
|
|
661
|
-
createdAt: new Date(),
|
|
662
|
-
},
|
|
663
|
-
{
|
|
664
|
-
author: 'agent-bot',
|
|
665
|
-
content:
|
|
666
|
-
'From: :robot: agent report\n\n```json\n{ "pullRequestRequired": false, "nextStep": null }\n```\n',
|
|
667
|
-
createdAt: new Date(),
|
|
668
|
-
},
|
|
669
|
-
]);
|
|
670
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
671
|
-
|
|
672
|
-
await useCase.run({
|
|
673
|
-
projectUrl: 'https://github.com/user/repo',
|
|
674
|
-
preparationProcessCheckCommand: 'pgrep -fa "claude-agent.*{URL}"',
|
|
675
|
-
thresholdForAutoReject: 3,
|
|
676
|
-
allowedIssueAuthors: ['agent-bot'],
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
expect(mockIssueRepository.findRelatedOpenPRs.mock.calls).toHaveLength(0);
|
|
680
|
-
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
681
|
-
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('4');
|
|
682
|
-
expect(mockIssueCommentRepository.createComment.mock.calls).toHaveLength(0);
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
it('should not re-dispatch the declared agent when a later report omits the next step agent an earlier report declared', async () => {
|
|
686
|
-
const stuckIssue = createMockIssue({
|
|
687
|
-
url: 'https://github.com/user/repo/issues/10',
|
|
688
|
-
status: 'Preparation',
|
|
689
|
-
});
|
|
690
|
-
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
691
|
-
project: mockProject,
|
|
692
|
-
issues: [stuckIssue],
|
|
693
|
-
cacheUsed: false,
|
|
694
|
-
});
|
|
695
|
-
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
696
|
-
stdout: '',
|
|
697
|
-
stderr: '',
|
|
698
|
-
exitCode: 1,
|
|
699
|
-
});
|
|
700
|
-
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
701
|
-
{
|
|
702
|
-
author: 'agent-bot',
|
|
703
|
-
content:
|
|
704
|
-
'From: :robot: agent report\n\n```json\n{ "nextStepAgent": "pr-reviewer" }\n```\n',
|
|
705
|
-
createdAt: new Date(),
|
|
706
|
-
},
|
|
707
|
-
{
|
|
708
|
-
author: 'agent-bot',
|
|
709
|
-
content:
|
|
710
|
-
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE\nThe last report declared that this task needs no pull request.',
|
|
711
|
-
createdAt: new Date(),
|
|
712
|
-
},
|
|
713
|
-
{
|
|
714
|
-
author: 'agent-bot',
|
|
715
|
-
content:
|
|
716
|
-
'From: :robot: agent report\n\n```json\n{ "pullRequestRequired": false, "reviewResult": "PASS", "nextStep": null }\n```\n',
|
|
717
|
-
createdAt: new Date(),
|
|
718
|
-
},
|
|
719
|
-
]);
|
|
720
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
721
|
-
|
|
722
|
-
await useCase.run({
|
|
723
|
-
projectUrl: 'https://github.com/user/repo',
|
|
724
|
-
preparationProcessCheckCommand: 'pgrep -fa "claude-agent.*{URL}"',
|
|
725
|
-
thresholdForAutoReject: 3,
|
|
726
|
-
allowedIssueAuthors: ['agent-bot'],
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
expect(mockIssueRepository.setIssueAgentField.mock.calls).toHaveLength(0);
|
|
730
|
-
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
731
|
-
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('4');
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
it('should revert an orphaned issue to Awaiting Workspace when the no pull request declaration comes from an author outside allowedIssueAuthors', async () => {
|
|
735
|
-
const stuckIssue = createMockIssue({
|
|
736
|
-
url: 'https://github.com/user/repo/issues/10',
|
|
737
|
-
status: 'Preparation',
|
|
738
|
-
});
|
|
739
|
-
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
740
|
-
project: mockProject,
|
|
741
|
-
issues: [stuckIssue],
|
|
742
|
-
cacheUsed: false,
|
|
743
|
-
});
|
|
744
|
-
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
745
|
-
stdout: '',
|
|
746
|
-
stderr: '',
|
|
747
|
-
exitCode: 1,
|
|
748
|
-
});
|
|
749
|
-
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
750
|
-
{
|
|
751
|
-
author: 'stranger',
|
|
752
|
-
content:
|
|
753
|
-
'From: :robot: agent report\n\n```json\n{ "pullRequestRequired": false, "nextStep": null }\n```\n',
|
|
754
|
-
createdAt: new Date(),
|
|
755
|
-
},
|
|
756
|
-
]);
|
|
757
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
758
|
-
|
|
759
|
-
await useCase.run({
|
|
760
|
-
projectUrl: 'https://github.com/user/repo',
|
|
761
|
-
preparationProcessCheckCommand: 'pgrep -fa "claude-agent.*{URL}"',
|
|
762
|
-
thresholdForAutoReject: 3,
|
|
763
|
-
allowedIssueAuthors: ['agent-bot'],
|
|
764
|
-
});
|
|
765
|
-
|
|
766
|
-
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
767
|
-
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('1');
|
|
768
|
-
expect(mockIssueCommentRepository.createComment.mock.calls).toHaveLength(1);
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
it('should advance an orphaned issue to Awaiting Quality Check when its own status comments were written after the report declaring no pull request is required', async () => {
|
|
772
|
-
const stuckIssue = createMockIssue({
|
|
773
|
-
url: 'https://github.com/user/repo/issues/10',
|
|
774
|
-
status: 'Preparation',
|
|
775
|
-
});
|
|
776
|
-
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
777
|
-
project: mockProject,
|
|
778
|
-
issues: [stuckIssue],
|
|
779
|
-
cacheUsed: false,
|
|
780
|
-
});
|
|
781
|
-
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
782
|
-
stdout: '',
|
|
783
|
-
stderr: '',
|
|
784
|
-
exitCode: 1,
|
|
785
|
-
});
|
|
786
|
-
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
787
|
-
{
|
|
788
|
-
author: 'agent-bot',
|
|
789
|
-
content:
|
|
790
|
-
'From: :robot: agent report\n\n```json\n{ "pullRequestRequired": false, "nextStep": null }\n```\n',
|
|
791
|
-
createdAt: new Date(),
|
|
792
|
-
},
|
|
793
|
-
{
|
|
794
|
-
author: 'agent-bot',
|
|
795
|
-
content:
|
|
796
|
-
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE\nThe last report declared that this task needs no pull request.',
|
|
797
|
-
createdAt: new Date(),
|
|
798
|
-
},
|
|
799
|
-
{
|
|
800
|
-
author: 'agent-bot',
|
|
801
|
-
content: 'Auto Status Check: REJECTED\n- ORPHANED_PREPARATION',
|
|
802
|
-
createdAt: new Date(),
|
|
803
|
-
},
|
|
804
|
-
]);
|
|
805
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
806
|
-
|
|
807
|
-
await useCase.run({
|
|
808
|
-
projectUrl: 'https://github.com/user/repo',
|
|
809
|
-
preparationProcessCheckCommand: 'pgrep -fa "claude-agent.*{URL}"',
|
|
810
|
-
thresholdForAutoReject: 3,
|
|
811
|
-
allowedIssueAuthors: ['agent-bot'],
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
expect(mockIssueRepository.findRelatedOpenPRs.mock.calls).toHaveLength(0);
|
|
815
|
-
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
816
|
-
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('4');
|
|
817
|
-
expect(mockIssueCommentRepository.createComment.mock.calls).toHaveLength(0);
|
|
818
|
-
});
|
|
819
|
-
|
|
820
600
|
it('should keep rejecting an orphaned issue whose own status comments follow a report that declares nothing', async () => {
|
|
821
601
|
const stuckIssue = createMockIssue({
|
|
822
602
|
url: 'https://github.com/user/repo/issues/10',
|
|
@@ -2609,97 +2389,4 @@ describe('RevertOrphanedPreparationUseCase', () => {
|
|
|
2609
2389
|
'Auto Status Check: REJECTED',
|
|
2610
2390
|
);
|
|
2611
2391
|
});
|
|
2612
|
-
|
|
2613
|
-
it('should move an orphaned issue to Awaiting Quality Check with AWAITING_OWNER_APPROVAL when last report declares waitingForOwnerApproval', async () => {
|
|
2614
|
-
const stuckIssue = createMockIssue({
|
|
2615
|
-
url: 'https://github.com/user/repo/issues/10',
|
|
2616
|
-
status: 'Preparation',
|
|
2617
|
-
});
|
|
2618
|
-
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
2619
|
-
project: mockProject,
|
|
2620
|
-
issues: [stuckIssue],
|
|
2621
|
-
cacheUsed: false,
|
|
2622
|
-
});
|
|
2623
|
-
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
2624
|
-
stdout: '',
|
|
2625
|
-
stderr: '',
|
|
2626
|
-
exitCode: 1,
|
|
2627
|
-
});
|
|
2628
|
-
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
2629
|
-
{
|
|
2630
|
-
author: 'agent-bot',
|
|
2631
|
-
content:
|
|
2632
|
-
'From: :robot: systems-analyst (model)\n```json\n{"pullRequestRequired": false, "waitingForOwnerApproval": true}\n```',
|
|
2633
|
-
createdAt: new Date(),
|
|
2634
|
-
},
|
|
2635
|
-
]);
|
|
2636
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
2637
|
-
|
|
2638
|
-
await useCase.run({
|
|
2639
|
-
projectUrl: 'https://github.com/user/repo',
|
|
2640
|
-
preparationProcessCheckCommand: 'pgrep -fa "claude-agent.*{URL}"',
|
|
2641
|
-
thresholdForAutoReject: 3,
|
|
2642
|
-
allowedIssueAuthors: ['agent-bot'],
|
|
2643
|
-
});
|
|
2644
|
-
|
|
2645
|
-
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
2646
|
-
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('4');
|
|
2647
|
-
expect(mockIssueCommentRepository.createComment.mock.calls).toHaveLength(1);
|
|
2648
|
-
expect(mockIssueCommentRepository.createComment.mock.calls[0][1]).toContain(
|
|
2649
|
-
'Auto Status Check: AWAITING_OWNER_APPROVAL',
|
|
2650
|
-
);
|
|
2651
|
-
});
|
|
2652
|
-
|
|
2653
|
-
it('should escalate to Failed Preparation after ownerApprovalTimeoutCycles AWAITING_OWNER_APPROVAL messages in RevertOrphaned', async () => {
|
|
2654
|
-
const stuckIssue = createMockIssue({
|
|
2655
|
-
url: 'https://github.com/user/repo/issues/10',
|
|
2656
|
-
status: 'Preparation',
|
|
2657
|
-
});
|
|
2658
|
-
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
2659
|
-
project: mockProject,
|
|
2660
|
-
issues: [stuckIssue],
|
|
2661
|
-
cacheUsed: false,
|
|
2662
|
-
});
|
|
2663
|
-
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
2664
|
-
stdout: '',
|
|
2665
|
-
stderr: '',
|
|
2666
|
-
exitCode: 1,
|
|
2667
|
-
});
|
|
2668
|
-
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
2669
|
-
{
|
|
2670
|
-
author: 'agent-bot',
|
|
2671
|
-
content:
|
|
2672
|
-
'From: :robot: systems-analyst (model)\n```json\n{"pullRequestRequired": false, "waitingForOwnerApproval": true}\n```',
|
|
2673
|
-
createdAt: new Date(),
|
|
2674
|
-
},
|
|
2675
|
-
{
|
|
2676
|
-
author: 'agent-bot',
|
|
2677
|
-
content:
|
|
2678
|
-
'Auto Status Check: AWAITING_OWNER_APPROVAL\nThe last report declared that this task is waiting for owner approval. Returning to Awaiting Workspace for the next cycle.',
|
|
2679
|
-
createdAt: new Date(),
|
|
2680
|
-
},
|
|
2681
|
-
{
|
|
2682
|
-
author: 'agent-bot',
|
|
2683
|
-
content:
|
|
2684
|
-
'Auto Status Check: AWAITING_OWNER_APPROVAL\nThe last report declared that this task is waiting for owner approval. Returning to Awaiting Workspace for the next cycle.',
|
|
2685
|
-
createdAt: new Date(),
|
|
2686
|
-
},
|
|
2687
|
-
]);
|
|
2688
|
-
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
2689
|
-
|
|
2690
|
-
await useCase.run({
|
|
2691
|
-
projectUrl: 'https://github.com/user/repo',
|
|
2692
|
-
preparationProcessCheckCommand: 'pgrep -fa "claude-agent.*{URL}"',
|
|
2693
|
-
thresholdForAutoReject: 3,
|
|
2694
|
-
allowedIssueAuthors: ['agent-bot'],
|
|
2695
|
-
ownerApprovalTimeoutCycles: 2,
|
|
2696
|
-
});
|
|
2697
|
-
|
|
2698
|
-
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
2699
|
-
expect(mockIssueRepository.updateStatus.mock.calls[0][2]).toBe('5');
|
|
2700
|
-
expect(mockIssueCommentRepository.createComment.mock.calls).toHaveLength(1);
|
|
2701
|
-
expect(mockIssueCommentRepository.createComment.mock.calls[0][1]).toContain(
|
|
2702
|
-
'Owner approval was not received after 2 cycles. Moving to Failed Preparation.',
|
|
2703
|
-
);
|
|
2704
|
-
});
|
|
2705
2392
|
});
|