github-issue-tower-defence-management 1.174.2 → 1.175.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 +14 -0
- package/bin/adapter/entry-points/console/consoleDoneStore.js +0 -2
- package/bin/adapter/entry-points/console/consoleDoneStore.js.map +1 -1
- package/bin/adapter/entry-points/console/consoleTabNames.js +0 -1
- package/bin/adapter/entry-points/console/consoleTabNames.js.map +1 -1
- package/bin/adapter/entry-points/console/dashboardComposeService.js +2 -4
- package/bin/adapter/entry-points/console/dashboardComposeService.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-g4SqIzCh.js → index-BhDothvr.js} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/domain/entities/WorkflowStatus.js +1 -6
- package/bin/domain/entities/WorkflowStatus.js.map +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +4 -5
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +0 -39
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +4 -5
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js +15 -1
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js.map +1 -1
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js +0 -2
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js.map +1 -1
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js +2 -3
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js.map +1 -1
- package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js +0 -1
- package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js.map +1 -1
- package/bin/domain/usecases/findLastAgentDeclaringReport.js +12 -0
- package/bin/domain/usecases/findLastAgentDeclaringReport.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/console/consoleDoneStore.ts +0 -2
- package/src/adapter/entry-points/console/consoleTabNames.ts +0 -1
- package/src/adapter/entry-points/console/dashboardComposeService.test.ts +4 -8
- package/src/adapter/entry-points/console/dashboardComposeService.ts +1 -3
- package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +12 -17
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +4 -32
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +1 -18
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +3 -7
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +2 -4
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.test.ts +6 -8
- package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.test.ts +8 -17
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +0 -2
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +3 -3
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +5 -6
- package/src/adapter/entry-points/console/ui-dist/assets/{index-g4SqIzCh.js → index-BhDothvr.js} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/adapter/entry-points/console/webServer.test.ts +4 -6
- package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +30 -26
- package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +5 -6
- package/src/domain/entities/WorkflowStatus.ts +0 -5
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +32 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +7 -8
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +198 -863
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +0 -68
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +8 -11
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +89 -51
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +30 -2
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +62 -72
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +0 -8
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +19 -28
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +2 -3
- package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +11 -12
- package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +0 -3
- package/src/domain/usecases/findLastAgentDeclaringReport.test.ts +71 -0
- package/src/domain/usecases/findLastAgentDeclaringReport.ts +17 -0
- package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +2 -2
- package/types/adapter/entry-points/console/consoleDoneStore.d.ts.map +1 -1
- package/types/adapter/entry-points/console/consoleTabNames.d.ts.map +1 -1
- package/types/adapter/entry-points/console/dashboardComposeService.d.ts.map +1 -1
- package/types/domain/entities/WorkflowStatus.d.ts +0 -1
- package/types/domain/entities/WorkflowStatus.d.ts.map +1 -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.map +1 -1
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts +1 -0
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts.map +1 -1
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts +1 -2
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts.map +1 -1
- package/types/domain/usecases/dashboard/ComposeDashboardUseCase.d.ts.map +1 -1
- package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts +0 -1
- package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts.map +1 -1
- package/types/domain/usecases/findLastAgentDeclaringReport.d.ts +5 -0
- package/types/domain/usecases/findLastAgentDeclaringReport.d.ts.map +1 -0
|
@@ -17,7 +17,6 @@ const STORY_OPTIONS: FieldOption[] = [
|
|
|
17
17
|
];
|
|
18
18
|
|
|
19
19
|
const STATUS_OPTIONS: FieldOption[] = [
|
|
20
|
-
storyOption('st-unread', 'Unread', 'ORANGE'),
|
|
21
20
|
storyOption('st-aw', 'Awaiting Workspace', 'BLUE'),
|
|
22
21
|
storyOption('st-prep', 'Preparation', 'YELLOW'),
|
|
23
22
|
storyOption('st-failed', 'Failed Preparation', 'RED'),
|
|
@@ -114,45 +113,49 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
114
113
|
|
|
115
114
|
describe('common actionable filter', () => {
|
|
116
115
|
it('rejects closed issues', () => {
|
|
117
|
-
const result = run([
|
|
118
|
-
|
|
116
|
+
const result = run([
|
|
117
|
+
makeIssue({ status: 'Todo by human', isClosed: true }),
|
|
118
|
+
]);
|
|
119
|
+
expect(result['todo-by-human'].items).toHaveLength(0);
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
it('rejects issues not assigned to the assignee login', () => {
|
|
122
123
|
const result = run([
|
|
123
|
-
makeIssue({ status: '
|
|
124
|
+
makeIssue({ status: 'Todo by human', assignees: ['other-person'] }),
|
|
124
125
|
]);
|
|
125
|
-
expect(result.
|
|
126
|
+
expect(result['todo-by-human'].items).toHaveLength(0);
|
|
126
127
|
});
|
|
127
128
|
|
|
128
129
|
it('rejects issues with a depended issue url', () => {
|
|
129
130
|
const result = run([
|
|
130
131
|
makeIssue({
|
|
131
|
-
status: '
|
|
132
|
+
status: 'Todo by human',
|
|
132
133
|
dependedIssueUrls: ['https://github.com/demo/repo/issues/99'],
|
|
133
134
|
}),
|
|
134
135
|
]);
|
|
135
|
-
expect(result.
|
|
136
|
+
expect(result['todo-by-human'].items).toHaveLength(0);
|
|
136
137
|
});
|
|
137
138
|
|
|
138
139
|
it('rejects issues with a next action date', () => {
|
|
139
140
|
const result = run([
|
|
140
141
|
makeIssue({
|
|
141
|
-
status: '
|
|
142
|
+
status: 'Todo by human',
|
|
142
143
|
nextActionDate: new Date('2026-07-01T00:00:00.000Z'),
|
|
143
144
|
}),
|
|
144
145
|
]);
|
|
145
|
-
expect(result.
|
|
146
|
+
expect(result['todo-by-human'].items).toHaveLength(0);
|
|
146
147
|
});
|
|
147
148
|
|
|
148
149
|
it('rejects issues with a next action hour', () => {
|
|
149
|
-
const result = run([
|
|
150
|
-
|
|
150
|
+
const result = run([
|
|
151
|
+
makeIssue({ status: 'Todo by human', nextActionHour: 9 }),
|
|
152
|
+
]);
|
|
153
|
+
expect(result['todo-by-human'].items).toHaveLength(0);
|
|
151
154
|
});
|
|
152
155
|
|
|
153
156
|
it('accepts an issue that passes every actionable condition', () => {
|
|
154
|
-
const result = run([makeIssue({ status: '
|
|
155
|
-
expect(result.
|
|
157
|
+
const result = run([makeIssue({ status: 'Todo by human' })]);
|
|
158
|
+
expect(result['todo-by-human'].items).toHaveLength(1);
|
|
156
159
|
});
|
|
157
160
|
});
|
|
158
161
|
|
|
@@ -161,19 +164,11 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
161
164
|
const result = run([
|
|
162
165
|
makeIssue({ status: 'awaiting quality check' }),
|
|
163
166
|
makeIssue({ status: 'Awaiting Quality Check' }),
|
|
164
|
-
makeIssue({ status: '
|
|
167
|
+
makeIssue({ status: 'Awaiting Workspace' }),
|
|
165
168
|
]);
|
|
166
169
|
expect(result.prs.items).toHaveLength(2);
|
|
167
170
|
});
|
|
168
171
|
|
|
169
|
-
it('selects unread items case-insensitively', () => {
|
|
170
|
-
const result = run([
|
|
171
|
-
makeIssue({ status: 'UNREAD' }),
|
|
172
|
-
makeIssue({ status: 'Awaiting Quality Check' }),
|
|
173
|
-
]);
|
|
174
|
-
expect(result.unread.items).toHaveLength(1);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
172
|
it('selects failed preparation items only with exact case', () => {
|
|
178
173
|
const result = run([
|
|
179
174
|
makeIssue({ status: 'Failed Preparation' }),
|
|
@@ -189,7 +184,7 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
189
184
|
makeIssue({ status: 'Todo by human' }),
|
|
190
185
|
makeIssue({ status: 'Todo' }),
|
|
191
186
|
makeIssue({ status: 'todo by human' }),
|
|
192
|
-
makeIssue({ status: '
|
|
187
|
+
makeIssue({ status: 'Awaiting Workspace' }),
|
|
193
188
|
]);
|
|
194
189
|
expect(result['todo-by-human'].items.map((item) => item.number)).toEqual([
|
|
195
190
|
1, 2,
|
|
@@ -208,7 +203,7 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
208
203
|
makeIssue({ status: 'Todo by agent' }),
|
|
209
204
|
makeIssue({ status: 'todo by agent' }),
|
|
210
205
|
makeIssue({ status: 'Todo by human' }),
|
|
211
|
-
makeIssue({ status: '
|
|
206
|
+
makeIssue({ status: 'Awaiting Workspace' }),
|
|
212
207
|
]);
|
|
213
208
|
expect(result['todo-by-agent'].items.map((item) => item.number)).toEqual([
|
|
214
209
|
1,
|
|
@@ -428,7 +423,6 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
428
423
|
...result['workflow-blocker'].items,
|
|
429
424
|
...result.prs.items,
|
|
430
425
|
...result.triage.items,
|
|
431
|
-
...result.unread.items,
|
|
432
426
|
...result['failed-preparation'].items,
|
|
433
427
|
...result['todo-by-human'].items,
|
|
434
428
|
];
|
|
@@ -478,16 +472,15 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
478
472
|
story: 'regular / WORKFLOW BLOCKER',
|
|
479
473
|
status: 'In Tmux by human',
|
|
480
474
|
}),
|
|
481
|
-
makeIssue({ status: '
|
|
475
|
+
makeIssue({ story: 'no story', status: 'Awaiting Workspace' }),
|
|
482
476
|
]);
|
|
483
477
|
expect(
|
|
484
478
|
result['workflow-blocker'].items.map((item) => item.status).sort(),
|
|
485
479
|
).toEqual(['In Tmux by agent', 'In Tmux by human']);
|
|
486
|
-
expect(result.
|
|
480
|
+
expect(result.triage.items.map((item) => item.number)).toEqual([3]);
|
|
487
481
|
expect(
|
|
488
482
|
result.prs.items
|
|
489
483
|
.concat(result.triage.items)
|
|
490
|
-
.concat(result.unread.items)
|
|
491
484
|
.concat(result['failed-preparation'].items)
|
|
492
485
|
.concat(result['todo-by-human'].items)
|
|
493
486
|
.some((item) => item.status === 'In Tmux by agent'),
|
|
@@ -499,13 +492,13 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
499
492
|
it('projects the expected keys and never includes a body field', () => {
|
|
500
493
|
const result = run([
|
|
501
494
|
makeIssue({
|
|
502
|
-
status: '
|
|
495
|
+
status: 'Awaiting Quality Check',
|
|
503
496
|
story: 'Story Alpha',
|
|
504
497
|
labels: ['bug', 'p1'],
|
|
505
498
|
isPr: true,
|
|
506
499
|
}),
|
|
507
500
|
]);
|
|
508
|
-
const item = result.
|
|
501
|
+
const item = result.prs.items[0];
|
|
509
502
|
expect(Object.keys(item).sort()).toEqual(
|
|
510
503
|
[
|
|
511
504
|
'agent',
|
|
@@ -562,44 +555,46 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
562
555
|
it('emits null reactivation-trigger fields and an empty depended url array when absent', () => {
|
|
563
556
|
const result = run([
|
|
564
557
|
makeIssue({
|
|
565
|
-
status: '
|
|
558
|
+
status: 'Awaiting Quality Check',
|
|
566
559
|
nextActionDate: null,
|
|
567
560
|
nextActionHour: null,
|
|
568
561
|
dependedIssueUrls: [],
|
|
569
562
|
}),
|
|
570
563
|
]);
|
|
571
|
-
const item = result.
|
|
572
|
-
expect(item.status).toBe('
|
|
564
|
+
const item = result.prs.items[0];
|
|
565
|
+
expect(item.status).toBe('Awaiting Quality Check');
|
|
573
566
|
expect(item.nextActionDate).toBeNull();
|
|
574
567
|
expect(item.nextActionHour).toBeNull();
|
|
575
568
|
expect(item.dependedIssueUrls).toEqual([]);
|
|
576
569
|
});
|
|
577
570
|
|
|
578
571
|
it('maps a null story to an empty string', () => {
|
|
579
|
-
const result = run([
|
|
580
|
-
|
|
572
|
+
const result = run([
|
|
573
|
+
makeIssue({ status: 'Awaiting Quality Check', story: null }),
|
|
574
|
+
]);
|
|
575
|
+
expect(result.prs.items[0].story).toBe('');
|
|
581
576
|
});
|
|
582
577
|
|
|
583
578
|
it('serializes createdAt as an ISO string keeping milliseconds', () => {
|
|
584
579
|
const result = run([
|
|
585
580
|
makeIssue({
|
|
586
|
-
status: '
|
|
581
|
+
status: 'Awaiting Quality Check',
|
|
587
582
|
createdAt: new Date('2026-06-13T08:18:45.000Z'),
|
|
588
583
|
}),
|
|
589
584
|
]);
|
|
590
|
-
expect(result.
|
|
585
|
+
expect(result.prs.items[0].createdAt).toBe('2026-06-13T08:18:45.000Z');
|
|
591
586
|
});
|
|
592
587
|
});
|
|
593
588
|
|
|
594
589
|
describe('story order stable sort', () => {
|
|
595
590
|
it('sorts by story field order and places unknown stories last', () => {
|
|
596
591
|
const result = run([
|
|
597
|
-
makeIssue({ status: '
|
|
598
|
-
makeIssue({ status: '
|
|
599
|
-
makeIssue({ status: '
|
|
600
|
-
makeIssue({ status: '
|
|
592
|
+
makeIssue({ status: 'Todo by human', story: 'Story Beta' }),
|
|
593
|
+
makeIssue({ status: 'Todo by human', story: 'Unmapped Story' }),
|
|
594
|
+
makeIssue({ status: 'Todo by human', story: 'Story Alpha' }),
|
|
595
|
+
makeIssue({ status: 'Todo by human', story: 'Story Beta' }),
|
|
601
596
|
]);
|
|
602
|
-
expect(result.
|
|
597
|
+
expect(result['todo-by-human'].items.map((i) => i.story)).toEqual([
|
|
603
598
|
'Story Alpha',
|
|
604
599
|
'Story Beta',
|
|
605
600
|
'Story Beta',
|
|
@@ -609,10 +604,18 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
609
604
|
|
|
610
605
|
it('keeps original order between items sharing the same story (stable)', () => {
|
|
611
606
|
const result = run([
|
|
612
|
-
makeIssue({
|
|
613
|
-
|
|
607
|
+
makeIssue({
|
|
608
|
+
status: 'Todo by human',
|
|
609
|
+
story: 'Story Alpha',
|
|
610
|
+
title: 'first',
|
|
611
|
+
}),
|
|
612
|
+
makeIssue({
|
|
613
|
+
status: 'Todo by human',
|
|
614
|
+
story: 'Story Alpha',
|
|
615
|
+
title: 'second',
|
|
616
|
+
}),
|
|
614
617
|
]);
|
|
615
|
-
expect(result.
|
|
618
|
+
expect(result['todo-by-human'].items.map((i) => i.title)).toEqual([
|
|
616
619
|
'first',
|
|
617
620
|
'second',
|
|
618
621
|
]);
|
|
@@ -620,11 +623,11 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
620
623
|
|
|
621
624
|
it('groups items of different unknown stories contiguously even when interleaved by original position', () => {
|
|
622
625
|
const result = run([
|
|
623
|
-
makeIssue({ status: '
|
|
624
|
-
makeIssue({ status: '
|
|
625
|
-
makeIssue({ status: '
|
|
626
|
+
makeIssue({ status: 'Todo by human', story: 'Unknown Story A' }),
|
|
627
|
+
makeIssue({ status: 'Todo by human', story: 'Unknown Story B' }),
|
|
628
|
+
makeIssue({ status: 'Todo by human', story: 'Unknown Story A' }),
|
|
626
629
|
]);
|
|
627
|
-
expect(result.
|
|
630
|
+
expect(result['todo-by-human'].items.map((i) => i.story)).toEqual([
|
|
628
631
|
'Unknown Story A',
|
|
629
632
|
'Unknown Story A',
|
|
630
633
|
'Unknown Story B',
|
|
@@ -637,13 +640,6 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
637
640
|
const names = run([]).prs.statusOptions.map((o) => o.name);
|
|
638
641
|
expect(names).not.toContain('Awaiting Quality Check');
|
|
639
642
|
expect(names).not.toContain('Done');
|
|
640
|
-
expect(names).toContain('Unread');
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
it('excludes unread and done from unread status options', () => {
|
|
644
|
-
const names = run([]).unread.statusOptions.map((o) => o.name);
|
|
645
|
-
expect(names).not.toContain('Unread');
|
|
646
|
-
expect(names).not.toContain('Done');
|
|
647
643
|
expect(names).toContain('Awaiting Workspace');
|
|
648
644
|
});
|
|
649
645
|
|
|
@@ -656,7 +652,6 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
656
652
|
'Done',
|
|
657
653
|
'Preparation',
|
|
658
654
|
'Icebox',
|
|
659
|
-
'Unread',
|
|
660
655
|
'In Tmux by human',
|
|
661
656
|
'In Tmux by agent',
|
|
662
657
|
'Todo by agent',
|
|
@@ -700,12 +695,9 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
700
695
|
});
|
|
701
696
|
|
|
702
697
|
describe('storyColors shape per tab', () => {
|
|
703
|
-
it('uses object color values for prs
|
|
698
|
+
it('uses object color values for prs and failed-preparation', () => {
|
|
704
699
|
const result = run([]);
|
|
705
700
|
expect(result.prs.storyColors['Story Alpha']).toEqual({ color: 'BLUE' });
|
|
706
|
-
expect(result.unread.storyColors['Story Beta']).toEqual({
|
|
707
|
-
color: 'GREEN',
|
|
708
|
-
});
|
|
709
701
|
expect(result['failed-preparation'].storyColors['Story Alpha']).toEqual({
|
|
710
702
|
color: 'BLUE',
|
|
711
703
|
});
|
|
@@ -723,7 +715,6 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
723
715
|
const result = run([]);
|
|
724
716
|
expect(result.prs.generatedAt).toBe(generatedAt);
|
|
725
717
|
expect(result.triage.generatedAt).toBe(generatedAt);
|
|
726
|
-
expect(result.unread.generatedAt).toBe(generatedAt);
|
|
727
718
|
expect(result['failed-preparation'].generatedAt).toBe(generatedAt);
|
|
728
719
|
expect(generatedAt).not.toMatch(/\.\d{3}Z$/);
|
|
729
720
|
});
|
|
@@ -732,7 +723,6 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
732
723
|
const result = run([]);
|
|
733
724
|
expect(result.prs.pjcode).toBe('demo');
|
|
734
725
|
expect(result.triage.pjcode).toBe('demo');
|
|
735
|
-
expect(result.unread.pjcode).toBe('demo');
|
|
736
726
|
expect(result['failed-preparation'].pjcode).toBe('demo');
|
|
737
727
|
});
|
|
738
728
|
});
|
|
@@ -802,15 +792,11 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
802
792
|
const projectNoStory = baseProject(null);
|
|
803
793
|
|
|
804
794
|
it('degrades story order, colors and triage options gracefully', () => {
|
|
805
|
-
const result = run(
|
|
806
|
-
[makeIssue({ status: 'Unread', story: 'no story' })],
|
|
807
|
-
projectNoStory,
|
|
808
|
-
);
|
|
795
|
+
const result = run([makeIssue({ story: 'no story' })], projectNoStory);
|
|
809
796
|
expect(result.prs.storyOrder).toEqual([]);
|
|
810
797
|
expect(result.prs.storyColors).toEqual({});
|
|
811
798
|
expect(result.triage.storyOptions).toEqual([]);
|
|
812
799
|
expect(result.triage.storyColors).toEqual({});
|
|
813
|
-
expect(result.unread.items).toHaveLength(1);
|
|
814
800
|
expect(result.triage.items).toHaveLength(1);
|
|
815
801
|
});
|
|
816
802
|
});
|
|
@@ -902,13 +888,17 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
902
888
|
|
|
903
889
|
describe('agent field propagation', () => {
|
|
904
890
|
it('copies the agent value from the issue into the list item', () => {
|
|
905
|
-
const result = run([
|
|
906
|
-
|
|
891
|
+
const result = run([
|
|
892
|
+
makeIssue({ status: 'Awaiting Quality Check', agent: 'developer' }),
|
|
893
|
+
]);
|
|
894
|
+
expect(result.prs.items[0].agent).toBe('developer');
|
|
907
895
|
});
|
|
908
896
|
|
|
909
897
|
it('preserves null when the issue has no agent set', () => {
|
|
910
|
-
const result = run([
|
|
911
|
-
|
|
898
|
+
const result = run([
|
|
899
|
+
makeIssue({ status: 'Awaiting Quality Check', agent: null }),
|
|
900
|
+
]);
|
|
901
|
+
expect(result.prs.items[0].agent).toBeNull();
|
|
912
902
|
});
|
|
913
903
|
|
|
914
904
|
it('copies the agent into every tab that shows the issue', () => {
|
|
@@ -57,7 +57,6 @@ export type ConsoleTabName =
|
|
|
57
57
|
| 'workflow-blocker'
|
|
58
58
|
| 'prs'
|
|
59
59
|
| 'triage'
|
|
60
|
-
| 'unread'
|
|
61
60
|
| 'failed-preparation'
|
|
62
61
|
| 'todo-by-human'
|
|
63
62
|
| 'todo-by-agent'
|
|
@@ -83,7 +82,6 @@ export type ConsoleLists = {
|
|
|
83
82
|
'workflow-blocker': ConsoleStatusTab;
|
|
84
83
|
prs: ConsoleStatusTab;
|
|
85
84
|
triage: ConsoleTriageTab;
|
|
86
|
-
unread: ConsoleStatusTab;
|
|
87
85
|
'failed-preparation': ConsoleStatusTab;
|
|
88
86
|
'todo-by-human': ConsoleStatusTab;
|
|
89
87
|
'todo-by-agent': ConsoleStatusTab;
|
|
@@ -192,11 +190,6 @@ export class GenerateConsoleListsUseCase {
|
|
|
192
190
|
issue.status.toLowerCase() === 'awaiting quality check',
|
|
193
191
|
['awaiting quality check', 'done'],
|
|
194
192
|
),
|
|
195
|
-
unread: buildStatusTab(
|
|
196
|
-
(issue) =>
|
|
197
|
-
issue.status !== null && issue.status.toLowerCase() === 'unread',
|
|
198
|
-
['unread', 'done'],
|
|
199
|
-
),
|
|
200
193
|
'failed-preparation': buildStatusTab(
|
|
201
194
|
(issue) => issue.status === 'Failed Preparation',
|
|
202
195
|
[
|
|
@@ -204,7 +197,6 @@ export class GenerateConsoleListsUseCase {
|
|
|
204
197
|
'done',
|
|
205
198
|
'preparation',
|
|
206
199
|
'icebox',
|
|
207
|
-
'unread',
|
|
208
200
|
'in tmux by human',
|
|
209
201
|
'in tmux by agent',
|
|
210
202
|
'todo by agent',
|
|
@@ -19,7 +19,6 @@ import { TokenStatus, TokenStatusColor } from './GenerateTokenStatusUseCase';
|
|
|
19
19
|
const codePointLength = (value: string): number => [...value].length;
|
|
20
20
|
|
|
21
21
|
const projectRow = (overrides: Partial<DashboardRow>): DashboardRow => ({
|
|
22
|
-
unread: 0,
|
|
23
22
|
todo: 0,
|
|
24
23
|
qc: 0,
|
|
25
24
|
fail: 0,
|
|
@@ -198,11 +197,11 @@ describe('formatMachineStatusLines', () => {
|
|
|
198
197
|
|
|
199
198
|
describe('formatProjectHeaderLine', () => {
|
|
200
199
|
it('renders the fixed project grid header', () => {
|
|
201
|
-
expect(formatProjectHeaderLine()).toBe('pj
|
|
200
|
+
expect(formatProjectHeaderLine()).toBe('pj tdo aqc fal prp aws dep');
|
|
202
201
|
});
|
|
203
202
|
|
|
204
|
-
it('fits the 32 character width budget
|
|
205
|
-
expect(codePointLength(formatProjectHeaderLine())).
|
|
203
|
+
it('fits within the 32 character width budget', () => {
|
|
204
|
+
expect(codePointLength(formatProjectHeaderLine())).toBeLessThanOrEqual(
|
|
206
205
|
PROJECT_ROW_WIDTH_BUDGET,
|
|
207
206
|
);
|
|
208
207
|
});
|
|
@@ -213,42 +212,36 @@ describe('formatProjectRowLine', () => {
|
|
|
213
212
|
expect(
|
|
214
213
|
formatProjectRowLine({
|
|
215
214
|
code: 'ac',
|
|
216
|
-
row: projectRow({
|
|
215
|
+
row: projectRow({ todo: 1, qc: 2, ws: 4, dep: 1 }),
|
|
217
216
|
}),
|
|
218
|
-
).toBe('🟢ac
|
|
217
|
+
).toBe('🟢ac 1 2 0 0 4 1');
|
|
219
218
|
});
|
|
220
219
|
|
|
221
220
|
it('renders placeholder cells with a blank dot for an absent project file', () => {
|
|
222
221
|
expect(formatProjectRowLine({ code: 'in', row: null })).toBe(
|
|
223
|
-
' in -- -- -- -- -- --
|
|
222
|
+
' in -- -- -- -- -- --',
|
|
224
223
|
);
|
|
225
224
|
});
|
|
226
225
|
|
|
227
226
|
it('caps a count above 999 at 999', () => {
|
|
228
227
|
expect(
|
|
229
|
-
formatProjectRowLine({ code: 'ac', row: projectRow({
|
|
230
|
-
).toBe('
|
|
228
|
+
formatProjectRowLine({ code: 'ac', row: projectRow({ todo: 1500 }) }),
|
|
229
|
+
).toBe('🟢ac 999 0 0 0 0 0');
|
|
231
230
|
});
|
|
232
231
|
|
|
233
|
-
it('applies the
|
|
232
|
+
it('applies the four level severity dot rules in descending order', () => {
|
|
234
233
|
expect(
|
|
235
234
|
formatProjectRowLine({ code: 'ac', row: projectRow({ blocker: 2 }) }),
|
|
236
235
|
).toContain('🔴');
|
|
237
236
|
expect(
|
|
238
237
|
formatProjectRowLine({ code: 'ac', row: projectRow({ blocker: 1 }) }),
|
|
239
238
|
).toContain('🟣');
|
|
240
|
-
expect(
|
|
241
|
-
formatProjectRowLine({ code: 'ac', row: projectRow({ unread: 10 }) }),
|
|
242
|
-
).toContain('🟠');
|
|
243
239
|
expect(
|
|
244
240
|
formatProjectRowLine({ code: 'ac', row: projectRow({ qc: 15 }) }),
|
|
245
241
|
).toContain('🟠');
|
|
246
242
|
expect(
|
|
247
243
|
formatProjectRowLine({ code: 'ac', row: projectRow({ fail: 5 }) }),
|
|
248
244
|
).toContain('🟠');
|
|
249
|
-
expect(
|
|
250
|
-
formatProjectRowLine({ code: 'ac', row: projectRow({ unread: 5 }) }),
|
|
251
|
-
).toContain('🟡');
|
|
252
245
|
expect(
|
|
253
246
|
formatProjectRowLine({ code: 'ac', row: projectRow({ qc: 10 }) }),
|
|
254
247
|
).toContain('🟡');
|
|
@@ -258,7 +251,7 @@ describe('formatProjectRowLine', () => {
|
|
|
258
251
|
expect(
|
|
259
252
|
formatProjectRowLine({
|
|
260
253
|
code: 'ac',
|
|
261
|
-
row: projectRow({
|
|
254
|
+
row: projectRow({ qc: 9, fail: 2 }),
|
|
262
255
|
}),
|
|
263
256
|
).toContain('🟢');
|
|
264
257
|
});
|
|
@@ -267,7 +260,6 @@ describe('formatProjectRowLine', () => {
|
|
|
267
260
|
const present = formatProjectRowLine({
|
|
268
261
|
code: 'gl',
|
|
269
262
|
row: projectRow({
|
|
270
|
-
unread: 999,
|
|
271
263
|
todo: 999,
|
|
272
264
|
qc: 999,
|
|
273
265
|
fail: 999,
|
|
@@ -349,11 +341,11 @@ describe('ComposeDashboardUseCase', () => {
|
|
|
349
341
|
projects: [
|
|
350
342
|
{
|
|
351
343
|
code: 'ac',
|
|
352
|
-
row: projectRow({
|
|
344
|
+
row: projectRow({ todo: 1, qc: 2, ws: 4, dep: 1 }),
|
|
353
345
|
},
|
|
354
346
|
{
|
|
355
347
|
code: 'gl',
|
|
356
|
-
row: projectRow({
|
|
348
|
+
row: projectRow({ qc: 16, fail: 6, pr: 1 }),
|
|
357
349
|
},
|
|
358
350
|
{ code: 'in', row: null },
|
|
359
351
|
{ code: 'um', row: projectRow({ blocker: 1 }) },
|
|
@@ -395,11 +387,11 @@ describe('ComposeDashboardUseCase', () => {
|
|
|
395
387
|
const expectedBody =
|
|
396
388
|
'<tt>M55% C62% D89% cy14</tt><br>\n' +
|
|
397
389
|
'<tt>LA 16 23 40</tt><br>\n' +
|
|
398
|
-
'<tt>pj
|
|
399
|
-
'<tt>🟢ac
|
|
400
|
-
'<tt>🟠gl  
|
|
401
|
-
'<tt> in -- -- -- -- --  
|
|
402
|
-
'<tt>🟣um 0 0 0 0 0 0
|
|
390
|
+
'<tt>pj tdo aqc fal prp aws dep</tt><br>\n' +
|
|
391
|
+
'<tt>🟢ac 1 2 0 0 4 1</tt><br>\n' +
|
|
392
|
+
'<tt>🟠gl 0 16 6 1 0 0</tt><br>\n' +
|
|
393
|
+
'<tt> in -- -- -- -- -- --</tt><br>\n' +
|
|
394
|
+
'<tt>🟣um 0 0 0 0 0 0</tt><br>\n' +
|
|
403
395
|
'<tt></tt><br>\n' +
|
|
404
396
|
'<tt>⚪bob_ 100% 0d00h00 95% 0d02h00 0 0</tt><br>\n' +
|
|
405
397
|
'<tt>🟢alice 10% 0d01h00 12% 5d00h00 2 1</tt><br>\n' +
|
|
@@ -460,7 +452,6 @@ describe('ComposeDashboardUseCase', () => {
|
|
|
460
452
|
{
|
|
461
453
|
code: 'ac',
|
|
462
454
|
row: projectRow({
|
|
463
|
-
unread: 999,
|
|
464
455
|
todo: 999,
|
|
465
456
|
qc: 999,
|
|
466
457
|
fail: 999,
|
|
@@ -599,7 +590,7 @@ describe('ComposeDashboardUseCase seven day window aggregate line', () => {
|
|
|
599
590
|
sevenDayWindowAggregate: ComposeDashboardInput['sevenDayWindowAggregate'],
|
|
600
591
|
): ComposeDashboardInput => ({
|
|
601
592
|
machineStatus: null,
|
|
602
|
-
projects: [{ code: 'ac', row: projectRow({
|
|
593
|
+
projects: [{ code: 'ac', row: projectRow({ todo: 1 }) }],
|
|
603
594
|
tokens: [
|
|
604
595
|
tokenStatus({ name: 'alice', sevenDayUtilizationPercent: 60 }),
|
|
605
596
|
tokenStatus({ name: 'bob', sevenDayUtilizationPercent: 66 }),
|
|
@@ -633,7 +624,7 @@ describe('ComposeDashboardUseCase seven day window aggregate line', () => {
|
|
|
633
624
|
expect(lines[aggregateIndex - 1]).toBe(
|
|
634
625
|
formatProjectRowLine({
|
|
635
626
|
code: 'ac',
|
|
636
|
-
row: projectRow({
|
|
627
|
+
row: projectRow({ todo: 1 }),
|
|
637
628
|
}),
|
|
638
629
|
);
|
|
639
630
|
expect(lines[aggregateIndex + 1]).toContain('alice');
|
|
@@ -39,7 +39,6 @@ type ProjectColumn = {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
const PROJECT_COLUMNS: ProjectColumn[] = [
|
|
42
|
-
{ header: 'unr', key: 'unread' },
|
|
43
42
|
{ header: 'tdo', key: 'todo' },
|
|
44
43
|
{ header: 'aqc', key: 'qc' },
|
|
45
44
|
{ header: 'fal', key: 'fail' },
|
|
@@ -189,10 +188,10 @@ const severityDot = (row: DashboardRow): string => {
|
|
|
189
188
|
if (row.blocker === 1) {
|
|
190
189
|
return '🟣';
|
|
191
190
|
}
|
|
192
|
-
if (row.
|
|
191
|
+
if (row.qc >= 15 || row.fail >= 5) {
|
|
193
192
|
return '🟠';
|
|
194
193
|
}
|
|
195
|
-
if (row.
|
|
194
|
+
if (row.qc >= 10 || row.fail >= 3) {
|
|
196
195
|
return '🟡';
|
|
197
196
|
}
|
|
198
197
|
return '🟢';
|
|
@@ -45,7 +45,6 @@ describe('GenerateDashboardRowUseCase', () => {
|
|
|
45
45
|
|
|
46
46
|
it('returns all-zero counts for an empty issue list', () => {
|
|
47
47
|
expect(usecase.run({ issues: [], assigneeLogin: ASSIGNEE })).toEqual({
|
|
48
|
-
unread: 0,
|
|
49
48
|
todo: 0,
|
|
50
49
|
qc: 0,
|
|
51
50
|
fail: 0,
|
|
@@ -56,16 +55,14 @@ describe('GenerateDashboardRowUseCase', () => {
|
|
|
56
55
|
});
|
|
57
56
|
});
|
|
58
57
|
|
|
59
|
-
it('counts actionable
|
|
58
|
+
it('counts actionable Todo, Awaiting Quality Check and Awaiting Workspace issues', () => {
|
|
60
59
|
const issues = [
|
|
61
|
-
makeIssue({ status: 'Unread' }),
|
|
62
60
|
makeIssue({ status: 'Todo by human' }),
|
|
63
61
|
makeIssue({ status: 'Awaiting Quality Check' }),
|
|
64
62
|
makeIssue({ status: 'Awaiting Workspace' }),
|
|
65
63
|
];
|
|
66
64
|
|
|
67
65
|
expect(usecase.run({ issues, assigneeLogin: ASSIGNEE })).toEqual({
|
|
68
|
-
unread: 1,
|
|
69
66
|
todo: 1,
|
|
70
67
|
qc: 1,
|
|
71
68
|
fail: 0,
|
|
@@ -78,8 +75,6 @@ describe('GenerateDashboardRowUseCase', () => {
|
|
|
78
75
|
|
|
79
76
|
it('excludes non-actionable issues from actionable status columns', () => {
|
|
80
77
|
const issues = [
|
|
81
|
-
makeIssue({ status: 'Unread', nextActionDate: new Date() }),
|
|
82
|
-
makeIssue({ status: 'Unread', nextActionHour: 9 }),
|
|
83
78
|
makeIssue({
|
|
84
79
|
status: 'Awaiting Quality Check',
|
|
85
80
|
dependedIssueUrls: ['https://github.com/demo/repo/issues/999'],
|
|
@@ -89,7 +84,6 @@ describe('GenerateDashboardRowUseCase', () => {
|
|
|
89
84
|
];
|
|
90
85
|
|
|
91
86
|
expect(usecase.run({ issues, assigneeLogin: ASSIGNEE })).toEqual({
|
|
92
|
-
unread: 0,
|
|
93
87
|
todo: 0,
|
|
94
88
|
qc: 0,
|
|
95
89
|
fail: 0,
|
|
@@ -112,7 +106,6 @@ describe('GenerateDashboardRowUseCase', () => {
|
|
|
112
106
|
];
|
|
113
107
|
|
|
114
108
|
expect(usecase.run({ issues, assigneeLogin: ASSIGNEE })).toEqual({
|
|
115
|
-
unread: 0,
|
|
116
109
|
todo: 0,
|
|
117
110
|
qc: 0,
|
|
118
111
|
fail: 1,
|
|
@@ -140,16 +133,22 @@ describe('GenerateDashboardRowUseCase', () => {
|
|
|
140
133
|
|
|
141
134
|
it('counts blocker by case-insensitive workflow blocker story membership for non-closed mine issues', () => {
|
|
142
135
|
const issues = [
|
|
143
|
-
makeIssue({
|
|
136
|
+
makeIssue({
|
|
137
|
+
status: 'Awaiting Workspace',
|
|
138
|
+
story: 'Workflow Blocker / urgent',
|
|
139
|
+
}),
|
|
144
140
|
makeIssue({ status: 'Awaiting Workspace', story: 'workflow blocker' }),
|
|
145
|
-
makeIssue({ status: 'Unread', story: 'regular / maintenance' }),
|
|
146
141
|
makeIssue({
|
|
147
|
-
status: '
|
|
142
|
+
status: 'Awaiting Workspace',
|
|
143
|
+
story: 'regular / maintenance',
|
|
144
|
+
}),
|
|
145
|
+
makeIssue({
|
|
146
|
+
status: 'Awaiting Workspace',
|
|
148
147
|
story: 'workflow blocker',
|
|
149
148
|
isClosed: true,
|
|
150
149
|
}),
|
|
151
150
|
makeIssue({
|
|
152
|
-
status: '
|
|
151
|
+
status: 'Awaiting Workspace',
|
|
153
152
|
story: 'workflow blocker',
|
|
154
153
|
assignees: ['someone-else'],
|
|
155
154
|
}),
|
|
@@ -2,14 +2,12 @@ import { Issue } from '../../entities/Issue';
|
|
|
2
2
|
import {
|
|
3
3
|
AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
4
4
|
AWAITING_WORKSPACE_STATUS_NAME,
|
|
5
|
-
DEFAULT_STATUS_NAME,
|
|
6
5
|
FAILED_PREPARATION_STATUS_NAME,
|
|
7
6
|
PREPARATION_STATUS_NAME,
|
|
8
7
|
TODO_STATUS_NAME,
|
|
9
8
|
} from '../../entities/WorkflowStatus';
|
|
10
9
|
|
|
11
10
|
export type DashboardRow = {
|
|
12
|
-
unread: number;
|
|
13
11
|
todo: number;
|
|
14
12
|
qc: number;
|
|
15
13
|
fail: number;
|
|
@@ -48,7 +46,6 @@ export class GenerateDashboardRowUseCase {
|
|
|
48
46
|
.length;
|
|
49
47
|
|
|
50
48
|
return {
|
|
51
|
-
unread: countActionableWithStatus(DEFAULT_STATUS_NAME),
|
|
52
49
|
todo: countActionableWithStatus(TODO_STATUS_NAME),
|
|
53
50
|
qc: countActionableWithStatus(AWAITING_QUALITY_CHECK_STATUS_NAME),
|
|
54
51
|
fail: countMineWithStatus(FAILED_PREPARATION_STATUS_NAME),
|