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.
Files changed (82) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/adapter/entry-points/console/consoleDoneStore.js +0 -2
  3. package/bin/adapter/entry-points/console/consoleDoneStore.js.map +1 -1
  4. package/bin/adapter/entry-points/console/consoleTabNames.js +0 -1
  5. package/bin/adapter/entry-points/console/consoleTabNames.js.map +1 -1
  6. package/bin/adapter/entry-points/console/dashboardComposeService.js +2 -4
  7. package/bin/adapter/entry-points/console/dashboardComposeService.js.map +1 -1
  8. package/bin/adapter/entry-points/console/ui-dist/assets/{index-g4SqIzCh.js → index-BhDothvr.js} +1 -1
  9. package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
  10. package/bin/domain/entities/WorkflowStatus.js +1 -6
  11. package/bin/domain/entities/WorkflowStatus.js.map +1 -1
  12. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +4 -5
  13. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
  14. package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +0 -39
  15. package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
  16. package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +4 -5
  17. package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
  18. package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js +15 -1
  19. package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js.map +1 -1
  20. package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js +0 -2
  21. package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js.map +1 -1
  22. package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js +2 -3
  23. package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js.map +1 -1
  24. package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js +0 -1
  25. package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js.map +1 -1
  26. package/bin/domain/usecases/findLastAgentDeclaringReport.js +12 -0
  27. package/bin/domain/usecases/findLastAgentDeclaringReport.js.map +1 -0
  28. package/package.json +1 -1
  29. package/src/adapter/entry-points/console/consoleDoneStore.ts +0 -2
  30. package/src/adapter/entry-points/console/consoleTabNames.ts +0 -1
  31. package/src/adapter/entry-points/console/dashboardComposeService.test.ts +4 -8
  32. package/src/adapter/entry-points/console/dashboardComposeService.ts +1 -3
  33. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +12 -17
  34. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +4 -32
  35. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +1 -18
  36. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +3 -7
  37. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +2 -4
  38. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +1 -1
  39. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.test.ts +6 -8
  40. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.test.ts +8 -17
  41. package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +0 -2
  42. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +3 -3
  43. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +5 -6
  44. package/src/adapter/entry-points/console/ui-dist/assets/{index-g4SqIzCh.js → index-BhDothvr.js} +1 -1
  45. package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
  46. package/src/adapter/entry-points/console/webServer.test.ts +4 -6
  47. package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +30 -26
  48. package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +5 -6
  49. package/src/domain/entities/WorkflowStatus.ts +0 -5
  50. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +32 -0
  51. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +7 -8
  52. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +198 -863
  53. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +0 -68
  54. package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +8 -11
  55. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +89 -51
  56. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +30 -2
  57. package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +62 -72
  58. package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +0 -8
  59. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +19 -28
  60. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +2 -3
  61. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +11 -12
  62. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +0 -3
  63. package/src/domain/usecases/findLastAgentDeclaringReport.test.ts +71 -0
  64. package/src/domain/usecases/findLastAgentDeclaringReport.ts +17 -0
  65. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +2 -2
  66. package/types/adapter/entry-points/console/consoleDoneStore.d.ts.map +1 -1
  67. package/types/adapter/entry-points/console/consoleTabNames.d.ts.map +1 -1
  68. package/types/adapter/entry-points/console/dashboardComposeService.d.ts.map +1 -1
  69. package/types/domain/entities/WorkflowStatus.d.ts +0 -1
  70. package/types/domain/entities/WorkflowStatus.d.ts.map +1 -1
  71. package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
  72. package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts.map +1 -1
  73. package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts.map +1 -1
  74. package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts +1 -0
  75. package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts.map +1 -1
  76. package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts +1 -2
  77. package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts.map +1 -1
  78. package/types/domain/usecases/dashboard/ComposeDashboardUseCase.d.ts.map +1 -1
  79. package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts +0 -1
  80. package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts.map +1 -1
  81. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts +5 -0
  82. 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([makeIssue({ status: 'Unread', isClosed: true })]);
118
- expect(result.unread.items).toHaveLength(0);
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: 'Unread', assignees: ['other-person'] }),
124
+ makeIssue({ status: 'Todo by human', assignees: ['other-person'] }),
124
125
  ]);
125
- expect(result.unread.items).toHaveLength(0);
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: 'Unread',
132
+ status: 'Todo by human',
132
133
  dependedIssueUrls: ['https://github.com/demo/repo/issues/99'],
133
134
  }),
134
135
  ]);
135
- expect(result.unread.items).toHaveLength(0);
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: 'Unread',
142
+ status: 'Todo by human',
142
143
  nextActionDate: new Date('2026-07-01T00:00:00.000Z'),
143
144
  }),
144
145
  ]);
145
- expect(result.unread.items).toHaveLength(0);
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([makeIssue({ status: 'Unread', nextActionHour: 9 })]);
150
- expect(result.unread.items).toHaveLength(0);
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: 'Unread' })]);
155
- expect(result.unread.items).toHaveLength(1);
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: 'Unread' }),
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: 'Unread' }),
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: 'Unread' }),
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: 'Unread' }),
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.unread.items.map((item) => item.number)).toEqual([3]);
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: 'Unread',
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.unread.items[0];
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: 'Unread',
558
+ status: 'Awaiting Quality Check',
566
559
  nextActionDate: null,
567
560
  nextActionHour: null,
568
561
  dependedIssueUrls: [],
569
562
  }),
570
563
  ]);
571
- const item = result.unread.items[0];
572
- expect(item.status).toBe('Unread');
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([makeIssue({ status: 'Unread', story: null })]);
580
- expect(result.unread.items[0].story).toBe('');
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: 'Unread',
581
+ status: 'Awaiting Quality Check',
587
582
  createdAt: new Date('2026-06-13T08:18:45.000Z'),
588
583
  }),
589
584
  ]);
590
- expect(result.unread.items[0].createdAt).toBe('2026-06-13T08:18:45.000Z');
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: 'Unread', story: 'Story Beta' }),
598
- makeIssue({ status: 'Unread', story: 'Unmapped Story' }),
599
- makeIssue({ status: 'Unread', story: 'Story Alpha' }),
600
- makeIssue({ status: 'Unread', story: 'Story Beta' }),
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.unread.items.map((i) => i.story)).toEqual([
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({ status: 'Unread', story: 'Story Alpha', title: 'first' }),
613
- makeIssue({ status: 'Unread', story: 'Story Alpha', title: 'second' }),
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.unread.items.map((i) => i.title)).toEqual([
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: 'Unread', story: 'Unknown Story A' }),
624
- makeIssue({ status: 'Unread', story: 'Unknown Story B' }),
625
- makeIssue({ status: 'Unread', story: 'Unknown Story A' }),
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.unread.items.map((i) => i.story)).toEqual([
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, unread and failed-preparation', () => {
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([makeIssue({ status: 'Unread', agent: 'developer' })]);
906
- expect(result.unread.items[0].agent).toBe('developer');
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([makeIssue({ status: 'Unread', agent: null })]);
911
- expect(result.unread.items[0].agent).toBeNull();
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 unr tdo aqc fal prp aws dep');
200
+ expect(formatProjectHeaderLine()).toBe('pj tdo aqc fal prp aws dep');
202
201
  });
203
202
 
204
- it('fits the 32 character width budget exactly', () => {
205
- expect(codePointLength(formatProjectHeaderLine())).toBe(
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({ unread: 3, todo: 1, qc: 2, ws: 4, dep: 1 }),
215
+ row: projectRow({ todo: 1, qc: 2, ws: 4, dep: 1 }),
217
216
  }),
218
- ).toBe('🟢ac 3 1 2 0 0 4 1');
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({ unread: 1500 }) }),
230
- ).toBe('🟠ac 999 0 0 0 0 0 0');
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 five level severity dot rules in descending order', () => {
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({ unread: 4, qc: 9, fail: 2 }),
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({ unread: 3, todo: 1, qc: 2, ws: 4, dep: 1 }),
344
+ row: projectRow({ todo: 1, qc: 2, ws: 4, dep: 1 }),
353
345
  },
354
346
  {
355
347
  code: 'gl',
356
- row: projectRow({ unread: 12, qc: 16, fail: 6, pr: 1 }),
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%&nbsp;C62%&nbsp;D89%&nbsp;cy14</tt><br>\n' +
397
389
  '<tt>LA&nbsp;16&nbsp;23&nbsp;40</tt><br>\n' +
398
- '<tt>pj&nbsp;&nbsp;&nbsp;unr&nbsp;tdo&nbsp;aqc&nbsp;fal&nbsp;prp&nbsp;aws&nbsp;dep</tt><br>\n' +
399
- '<tt>🟢ac&nbsp;&nbsp;&nbsp;3&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;1</tt><br>\n' +
400
- '<tt>🟠gl&nbsp;&nbsp;12&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;16&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0</tt><br>\n' +
401
- '<tt>&nbsp;&nbsp;in&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--</tt><br>\n' +
402
- '<tt>🟣um&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0</tt><br>\n' +
390
+ '<tt>pj&nbsp;&nbsp;&nbsp;tdo&nbsp;aqc&nbsp;fal&nbsp;prp&nbsp;aws&nbsp;dep</tt><br>\n' +
391
+ '<tt>🟢ac&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;1</tt><br>\n' +
392
+ '<tt>🟠gl&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;16&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0</tt><br>\n' +
393
+ '<tt>&nbsp;&nbsp;in&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--&nbsp;&nbsp;--</tt><br>\n' +
394
+ '<tt>🟣um&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;0</tt><br>\n' +
403
395
  '<tt></tt><br>\n' +
404
396
  '<tt>⚪bob_&nbsp;100%&nbsp;0d00h00&nbsp;&nbsp;95%&nbsp;0d02h00&nbsp;0&nbsp;0</tt><br>\n' +
405
397
  '<tt>🟢alice&nbsp;&nbsp;10%&nbsp;0d01h00&nbsp;&nbsp;12%&nbsp;5d00h00&nbsp;2&nbsp;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({ unread: 1 }) }],
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({ unread: 1 }),
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.unread >= 10 || row.qc >= 15 || row.fail >= 5) {
191
+ if (row.qc >= 15 || row.fail >= 5) {
193
192
  return '🟠';
194
193
  }
195
- if (row.unread >= 5 || row.qc >= 10 || row.fail >= 3) {
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 Unread, Todo, Awaiting Quality Check and Awaiting Workspace issues', () => {
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({ status: 'Unread', story: 'Workflow Blocker / urgent' }),
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: 'Unread',
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: 'Unread',
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),