github-issue-tower-defence-management 1.143.1 → 1.143.2

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 (32) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +2 -2
  3. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +2 -9
  4. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  5. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js +2 -5
  6. package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
  7. package/bin/adapter/repositories/TmuxSilentSessionNotificationRepository.js +2 -1
  8. package/bin/adapter/repositories/TmuxSilentSessionNotificationRepository.js.map +1 -1
  9. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js +1 -1
  10. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js.map +1 -1
  11. package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +1 -35
  12. package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
  13. package/package.json +1 -1
  14. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -5
  15. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +3 -8
  16. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +0 -8
  17. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.test.ts +35 -4
  18. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts +2 -1
  19. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +0 -6
  20. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +21 -1
  21. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +1 -1
  22. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +21 -98
  23. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +0 -44
  24. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  25. package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts +0 -1
  26. package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts.map +1 -1
  27. package/types/adapter/repositories/TmuxSilentSessionNotificationRepository.d.ts.map +1 -1
  28. package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts +1 -3
  29. package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
  30. package/src/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.test.ts +0 -127
  31. package/src/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.ts +0 -108
  32. package/src/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.ts +0 -10
@@ -34,7 +34,6 @@ import {
34
34
  } from '../../repositories/ConfigurableSilentSessionMessageComposer';
35
35
  import { RealSleeper } from '../../repositories/RealSleeper';
36
36
  import { FileSystemSilentSessionCandidateStateRepository } from '../../repositories/FileSystemSilentSessionCandidateStateRepository';
37
- import { FileSystemSilentSessionNotifiedStateRepository } from '../../repositories/FileSystemSilentSessionNotifiedStateRepository';
38
37
  import { FileSystemSilentSessionHubTaskStatusCacheRepository } from '../../repositories/FileSystemSilentSessionHubTaskStatusCacheRepository';
39
38
 
40
39
  export type NotifySilentTmuxSessionsParams = {
@@ -53,7 +52,6 @@ export type NotifySilentTmuxSessionsParams = {
53
52
  staggerSeconds: number;
54
53
  candidateDebounceRecencyWindowSeconds: number;
55
54
  candidateDebounceStateFilePath: string | null;
56
- notifiedStateFilePath: string | null;
57
55
  activeHubTaskStatus: string | null;
58
56
  hubTaskStatusResolver: HubTaskStatusResolver | null;
59
57
  hubTaskStatusCacheStateFilePath: string | null;
@@ -118,7 +116,6 @@ export const notifySilentTmuxSessions = async (
118
116
  staggerSeconds,
119
117
  candidateDebounceRecencyWindowSeconds,
120
118
  candidateDebounceStateFilePath,
121
- notifiedStateFilePath,
122
119
  activeHubTaskStatus,
123
120
  hubTaskStatusResolver,
124
121
  hubTaskStatusCacheStateFilePath,
@@ -158,11 +155,6 @@ export const notifySilentTmuxSessions = async (
158
155
  candidateDebounceStateFilePath,
159
156
  )
160
157
  : new FileSystemSilentSessionCandidateStateRepository(),
161
- notifiedStateFilePath !== null
162
- ? new FileSystemSilentSessionNotifiedStateRepository(
163
- notifiedStateFilePath,
164
- )
165
- : new FileSystemSilentSessionNotifiedStateRepository(),
166
158
  messageComposer,
167
159
  new RealSleeper(),
168
160
  hubTaskStatusResolver,
@@ -38,7 +38,7 @@ describe('TmuxSilentSessionNotificationRepository', () => {
38
38
  ]);
39
39
  });
40
40
 
41
- it('preserves an unchanged multi-line message body inside the bracketed-paste frame', async () => {
41
+ it('flattens a multi-line message into a single line so no newline reaches the input box', async () => {
42
42
  const runner = createMockRunner();
43
43
  const repository = new TmuxSilentSessionNotificationRepository(runner);
44
44
  const multiLineMessage = 'first line\nsecond line\n\nthird line';
@@ -49,14 +49,45 @@ describe('TmuxSilentSessionNotificationRepository', () => {
49
49
  );
50
50
 
51
51
  const literalArgument = runner.runCommand.mock.calls[0][1][4];
52
- expect(literalArgument.startsWith('\x1b[200~')).toBe(true);
53
- expect(literalArgument.endsWith('\x1b[201~')).toBe(true);
52
+ expect(literalArgument).not.toContain('\n');
53
+ expect(literalArgument).not.toContain('\r');
54
54
  expect(
55
55
  literalArgument.slice('\x1b[200~'.length, -'\x1b[201~'.length),
56
- ).toBe(multiLineMessage);
56
+ ).toBe('first line second line third line');
57
+ });
58
+
59
+ it('keeps the bracketed-paste framing around the flattened message', async () => {
60
+ const runner = createMockRunner();
61
+ const repository = new TmuxSilentSessionNotificationRepository(runner);
62
+
63
+ await repository.sendSelfCheckNotification(
64
+ 'https_//github_com/owner/repo/issues/9',
65
+ 'first line\nsecond line',
66
+ );
67
+
68
+ const literalArgument = runner.runCommand.mock.calls[0][1][4];
57
69
  const literalBytes = Buffer.from(literalArgument, 'utf8').toString('hex');
58
70
  expect(literalBytes.startsWith('1b5b3230307e')).toBe(true);
59
71
  expect(literalBytes.endsWith('1b5b3230317e')).toBe(true);
72
+ expect(runner.runCommand.mock.calls[1]).toEqual([
73
+ 'tmux',
74
+ ['send-keys', '-t', 'https_//github_com/owner/repo/issues/9', 'Enter'],
75
+ ]);
76
+ });
77
+
78
+ it('collapses the run of spaces left by consecutive line breaks', async () => {
79
+ const runner = createMockRunner();
80
+ const repository = new TmuxSilentSessionNotificationRepository(runner);
81
+
82
+ await repository.sendSelfCheckNotification(
83
+ 'https_//github_com/owner/repo/issues/9',
84
+ 'section one\n\n\nsection two',
85
+ );
86
+
87
+ const literalArgument = runner.runCommand.mock.calls[0][1][4];
88
+ expect(
89
+ literalArgument.slice('\x1b[200~'.length, -'\x1b[201~'.length),
90
+ ).toBe('section one section two');
60
91
  });
61
92
 
62
93
  it('throws when sending the message literally fails', async () => {
@@ -11,7 +11,8 @@ export class TmuxSilentSessionNotificationRepository implements SilentSessionNot
11
11
  sessionName: string,
12
12
  message: string,
13
13
  ): Promise<void> => {
14
- const framedMessage = `${BRACKETED_PASTE_START}${message}${BRACKETED_PASTE_END}`;
14
+ const singleLineMessage = message.replace(/\s*[\r\n]+\s*/g, ' ').trim();
15
+ const framedMessage = `${BRACKETED_PASTE_START}${singleLineMessage}${BRACKETED_PASTE_END}`;
15
16
  const literalResult = await this.localCommandRunner.runCommand('tmux', [
16
17
  'send-keys',
17
18
  '-t',
@@ -709,12 +709,6 @@ describe('TranscriptOwnerCallStatusProvider wired into NotifySilentLiveSessionsU
709
709
  .mockResolvedValue(new EveryNameRecentSet()),
710
710
  saveCandidateSessionNames: jest.fn().mockResolvedValue(undefined),
711
711
  },
712
- {
713
- loadRecentNotifiedSessionNames: jest
714
- .fn()
715
- .mockResolvedValue(new Set<string>()),
716
- saveNotifiedSessionNames: jest.fn().mockResolvedValue(undefined),
717
- },
718
712
  {
719
713
  composeMainStalledSection: jest.fn().mockReturnValue('MAIN_STALLED'),
720
714
  composeMainStalledWithStaleOwnerCallSection: jest
@@ -28,7 +28,7 @@ describe('DefaultSilentSessionMessageComposer', () => {
28
28
  expect(section).toContain('This is an automated status check.');
29
29
  expect(section).toContain('No output has been observed for 10 minutes.');
30
30
  expect(section).toContain(
31
- 'If you are waiting on an external process, no action is needed — please log one line explaining the wait.',
31
+ 'If you are waiting on an automated process that runs without the owner',
32
32
  );
33
33
  expect(section).toContain('Otherwise please continue with the next step');
34
34
  expect(section).toContain('1.');
@@ -37,6 +37,26 @@ describe('DefaultSilentSessionMessageComposer', () => {
37
37
  expect(section).toContain('4.');
38
38
  });
39
39
 
40
+ it('limits the no-action wait to an automated process that runs without the owner', () => {
41
+ const section = composer.composeMainStalledSection(600);
42
+ expect(section).toContain(
43
+ 'If you are waiting on an automated process that runs without the owner - a continuous-integration run, an external API, or another process - no action is needed beyond logging one line explaining the wait.',
44
+ );
45
+ expect(section).not.toContain(
46
+ 'If you are waiting on an external process, no action is needed',
47
+ );
48
+ });
49
+
50
+ it('asks for an owner-call when the session is waiting on something only the owner can supply', () => {
51
+ const section = composer.composeMainStalledSection(600);
52
+ expect(section).toContain(
53
+ 'If you are waiting on something only the owner can supply - a decision, an approval, a credential, or an answer - please raise an owner-call now',
54
+ );
55
+ expect(section).toContain(
56
+ 'that wait is resolved only after the owner has been notified',
57
+ );
58
+ });
59
+
40
60
  it('states each of the four checkpoints exactly once', () => {
41
61
  const section = composer.composeMainStalledSection(600);
42
62
  const occurrences = (needle: string): number =>
@@ -57,7 +57,7 @@ export const composeMainStalledSelfDiagnosisGuidance = (): string => {
57
57
  const composeMainStalledMessage = (mainSilentSeconds: number): string => {
58
58
  const minutes = Math.floor(mainSilentSeconds / 60);
59
59
  return [
60
- `${SILENT_SESSION_REMINDER_SENTINEL} This is an automated status check. No output has been observed for ${minutes} minutes. If you are waiting on an external process, no action is needed please log one line explaining the wait. Otherwise please continue with the next step, with these points in mind:`,
60
+ `${SILENT_SESSION_REMINDER_SENTINEL} This is an automated status check. No output has been observed for ${minutes} minutes. If you are waiting on an automated process that runs without the owner - a continuous-integration run, an external API, or another process - no action is needed beyond logging one line explaining the wait. If you are waiting on something only the owner can supply - a decision, an approval, a credential, or an answer - please raise an owner-call now, because that wait is resolved only after the owner has been notified. Otherwise please continue with the next step, with these points in mind:`,
61
61
  `1. Keep the session task list current, marking finished items as done.`,
62
62
  `2. Run independent pieces of work in parallel across sub-agents.`,
63
63
  `3. Keep a monitor in place that notices when a sub-agent has produced no output for about 5 minutes.`,
@@ -20,7 +20,6 @@ import { OwnerCallStatusProvider } from './adapter-interfaces/OwnerCallStatusPro
20
20
  import { RefusalTailStatusProvider } from './adapter-interfaces/RefusalTailStatusProvider';
21
21
  import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
22
22
  import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
23
- import { SilentSessionNotifiedStateRepository } from './adapter-interfaces/SilentSessionNotifiedStateRepository';
24
23
  import { SilentSessionHubTaskStatusCacheRepository } from './adapter-interfaces/SilentSessionHubTaskStatusCacheRepository';
25
24
  import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
26
25
  import { Sleeper } from './adapter-interfaces/Sleeper';
@@ -50,7 +49,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
50
49
  let mockOwnerCallStatusProvider: Mocked<OwnerCallStatusProvider>;
51
50
  let mockNotificationRepository: Mocked<SilentSessionNotificationRepository>;
52
51
  let mockCandidateStateRepository: Mocked<SilentSessionCandidateStateRepository>;
53
- let mockNotifiedStateRepository: Mocked<SilentSessionNotifiedStateRepository>;
54
52
  let mockMessageComposer: Mocked<SilentSessionMessageComposer>;
55
53
  let mockSleeper: Mocked<Sleeper>;
56
54
  let mockHubTaskStatusResolver: Mocked<HubTaskStatusResolver>;
@@ -170,12 +168,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
170
168
  .mockResolvedValue(everyNameRecentSet()),
171
169
  saveCandidateSessionNames: jest.fn().mockResolvedValue(undefined),
172
170
  };
173
- mockNotifiedStateRepository = {
174
- loadRecentNotifiedSessionNames: jest
175
- .fn()
176
- .mockResolvedValue(new Set<string>()),
177
- saveNotifiedSessionNames: jest.fn().mockResolvedValue(undefined),
178
- };
179
171
  mockMessageComposer = {
180
172
  composeMainStalledSection: jest
181
173
  .fn()
@@ -208,7 +200,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
208
200
  mockOwnerCallStatusProvider,
209
201
  mockNotificationRepository,
210
202
  mockCandidateStateRepository,
211
- mockNotifiedStateRepository,
212
203
  mockMessageComposer,
213
204
  mockSleeper,
214
205
  mockHubTaskStatusResolver,
@@ -217,20 +208,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
217
208
  );
218
209
  });
219
210
 
220
- // Simulates the on-disk fire-once latch across the per-cycle fresh monitor
221
- // process: load returns the currently persisted set, save overwrites it.
222
- const wireStatefulNotifiedLatch = (): void => {
223
- let persistedNotifiedSessionNames = new Set<string>();
224
- mockNotifiedStateRepository.loadRecentNotifiedSessionNames.mockImplementation(
225
- async () => new Set(persistedNotifiedSessionNames),
226
- );
227
- mockNotifiedStateRepository.saveNotifiedSessionNames.mockImplementation(
228
- async ({ sessionNames }) => {
229
- persistedNotifiedSessionNames = new Set(sessionNames);
230
- },
231
- );
232
- };
233
-
234
211
  const issueFor = (overrides: Partial<Issue>): Issue => ({
235
212
  nameWithOwner: 'HiromiShikata/repo',
236
213
  number: 42,
@@ -803,33 +780,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
803
780
  });
804
781
  });
805
782
 
806
- it('notifies a persistent stall only once per silent episode instead of every cycle', async () => {
807
- wireStatefulNotifiedLatch();
808
- setupLiveInteractiveSession(GITHUB_SESSION);
809
- mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
810
- [
811
- {
812
- sessionName: GITHUB_SESSION,
813
- lastOutputEpochSeconds:
814
- nowEpochSeconds - DEFAULT_MAIN_SILENT_THRESHOLD_SECONDS,
815
- },
816
- ],
817
- );
818
-
819
- await useCase.run(runParams());
820
- await useCase.run(runParams());
821
- await useCase.run(runParams());
822
-
823
- expect(
824
- mockNotificationRepository.sendSelfCheckNotification,
825
- ).toHaveBeenCalledTimes(1);
826
- expect(
827
- mockNotificationRepository.sendSelfCheckNotification,
828
- ).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
829
- });
830
-
831
783
  it('re-notifies a session after its silent episode resolves and a new one begins', async () => {
832
- wireStatefulNotifiedLatch();
833
784
  setupLiveInteractiveSession(GITHUB_SESSION);
834
785
  const stalled = [
835
786
  {
@@ -968,7 +919,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
968
919
  });
969
920
  });
970
921
 
971
- describe('fire-once latch', () => {
922
+ describe('repeated delivery while a session stays silent', () => {
972
923
  const setupSilentGithubSession = (): void => {
973
924
  setupLiveInteractiveSession(GITHUB_SESSION);
974
925
  mockSessionOutputActivityRepository.listSessionOutputActivities.mockResolvedValue(
@@ -982,70 +933,45 @@ describe('NotifySilentLiveSessionsUseCase', () => {
982
933
  );
983
934
  };
984
935
 
985
- it('loads the fire-once latch using the configured recency window', async () => {
986
- setupSilentGithubSession();
987
-
988
- await useCase.run(
989
- runParams({ candidateDebounceRecencyWindowSeconds: 900 }),
990
- );
991
-
992
- expect(
993
- mockNotifiedStateRepository.loadRecentNotifiedSessionNames,
994
- ).toHaveBeenCalledWith({ now, recencyWindowSeconds: 900 });
995
- });
996
-
997
- it('persists the sent session in the fire-once latch on a send cycle', async () => {
936
+ it('sends the reminder again on the next cycle to a session that is still silent', async () => {
998
937
  setupSilentGithubSession();
999
938
 
939
+ await useCase.run(runParams());
1000
940
  await useCase.run(runParams());
1001
941
 
1002
942
  expect(
1003
943
  mockNotificationRepository.sendSelfCheckNotification,
1004
- ).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
1005
- expect(
1006
- mockNotifiedStateRepository.saveNotifiedSessionNames,
1007
- ).toHaveBeenCalledWith({ sessionNames: [GITHUB_SESSION], now });
1008
- });
1009
-
1010
- it('does not re-send but keeps the latch for a session already notified this episode', async () => {
1011
- setupSilentGithubSession();
1012
- mockNotifiedStateRepository.loadRecentNotifiedSessionNames.mockResolvedValue(
1013
- new Set([GITHUB_SESSION]),
1014
- );
1015
-
1016
- await useCase.run(runParams());
1017
-
944
+ ).toHaveBeenCalledTimes(2);
1018
945
  expect(
1019
946
  mockNotificationRepository.sendSelfCheckNotification,
1020
- ).not.toHaveBeenCalled();
947
+ ).toHaveBeenNthCalledWith(1, GITHUB_SESSION, MAIN_STALLED_SECTION);
1021
948
  expect(
1022
- mockNotifiedStateRepository.saveNotifiedSessionNames,
1023
- ).toHaveBeenCalledWith({ sessionNames: [GITHUB_SESSION], now });
949
+ mockNotificationRepository.sendSelfCheckNotification,
950
+ ).toHaveBeenNthCalledWith(2, GITHUB_SESSION, MAIN_STALLED_SECTION);
1024
951
  });
1025
952
 
1026
- it('prunes the fire-once latch for a previously notified session that is no longer a candidate', async () => {
1027
- mockNotifiedStateRepository.loadRecentNotifiedSessionNames.mockResolvedValue(
1028
- new Set([GITHUB_SESSION]),
1029
- );
953
+ it('sends the reminder for a silent session even when its main REPL is mid-turn, because apparent busyness no longer defers delivery', async () => {
954
+ setupSilentGithubSession();
1030
955
 
1031
956
  await useCase.run(runParams());
1032
957
 
1033
958
  expect(
1034
- mockNotifiedStateRepository.saveNotifiedSessionNames,
1035
- ).toHaveBeenCalledWith({ sessionNames: [], now });
959
+ mockNotificationRepository.sendSelfCheckNotification,
960
+ ).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
1036
961
  });
1037
962
 
1038
- it('sends the reminder for a silent session even when its main REPL is mid-turn, because apparent busyness no longer defers delivery', async () => {
963
+ it('keeps sending the reminder on every consecutive cycle the session stays silent', async () => {
1039
964
  setupSilentGithubSession();
1040
965
 
966
+ await useCase.run(runParams());
967
+ await useCase.run(runParams());
968
+ await useCase.run(runParams());
969
+ await useCase.run(runParams());
1041
970
  await useCase.run(runParams());
1042
971
 
1043
972
  expect(
1044
973
  mockNotificationRepository.sendSelfCheckNotification,
1045
- ).toHaveBeenCalledWith(GITHUB_SESSION, MAIN_STALLED_SECTION);
1046
- expect(
1047
- mockNotifiedStateRepository.saveNotifiedSessionNames,
1048
- ).toHaveBeenCalledWith({ sessionNames: [GITHUB_SESSION], now });
974
+ ).toHaveBeenCalledTimes(5);
1049
975
  });
1050
976
  });
1051
977
 
@@ -1110,8 +1036,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
1110
1036
  ).toHaveBeenCalledWith(GITHUB_SESSION, SUBAGENT_SECTION);
1111
1037
  });
1112
1038
 
1113
- it('notifies a hung sub-agent only once per episode instead of every cycle', async () => {
1114
- wireStatefulNotifiedLatch();
1039
+ it('notifies a hung sub-agent again on every cycle it stays hung', async () => {
1115
1040
  setupSubAgents([hungSubAgent('sub-process-1')]);
1116
1041
 
1117
1042
  await useCase.run(runParams());
@@ -1119,7 +1044,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
1119
1044
 
1120
1045
  expect(
1121
1046
  mockNotificationRepository.sendSelfCheckNotification,
1122
- ).toHaveBeenCalledTimes(1);
1047
+ ).toHaveBeenCalledTimes(2);
1123
1048
  });
1124
1049
 
1125
1050
  it('never selects a waiting sub-agent for the long-running section even when quiet past both thresholds', async () => {
@@ -1160,8 +1085,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
1160
1085
  ).toHaveBeenCalledWith(GITHUB_SESSION, SUBAGENT_SECTION);
1161
1086
  });
1162
1087
 
1163
- it('notifies a quiet long-running sub-agent only once per episode while still evaluating it every cycle', async () => {
1164
- wireStatefulNotifiedLatch();
1088
+ it('notifies a quiet long-running sub-agent again on every cycle while still evaluating it every cycle', async () => {
1165
1089
  setupSubAgents([quietLongRunningSubAgent('sub-process-1', false)]);
1166
1090
 
1167
1091
  await useCase.run(runParams());
@@ -1169,7 +1093,7 @@ describe('NotifySilentLiveSessionsUseCase', () => {
1169
1093
 
1170
1094
  expect(
1171
1095
  mockNotificationRepository.sendSelfCheckNotification,
1172
- ).toHaveBeenCalledTimes(1);
1096
+ ).toHaveBeenCalledTimes(2);
1173
1097
  expect(
1174
1098
  mockMessageComposer.composeSubAgentSection,
1175
1099
  ).toHaveBeenNthCalledWith(2, {
@@ -1840,7 +1764,6 @@ describe('NotifySilentLiveSessionsUseCase', () => {
1840
1764
  mockOwnerCallStatusProvider,
1841
1765
  mockNotificationRepository,
1842
1766
  mockCandidateStateRepository,
1843
- mockNotifiedStateRepository,
1844
1767
  mockMessageComposer,
1845
1768
  mockSleeper,
1846
1769
  mockHubTaskStatusResolver,
@@ -9,7 +9,6 @@ import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionS
9
9
  import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
10
10
  import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
11
11
  import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
12
- import { SilentSessionNotifiedStateRepository } from './adapter-interfaces/SilentSessionNotifiedStateRepository';
13
12
  import { SilentSessionHubTaskStatusCacheRepository } from './adapter-interfaces/SilentSessionHubTaskStatusCacheRepository';
14
13
  import { Sleeper } from './adapter-interfaces/Sleeper';
15
14
  import { IssueRepository } from './adapter-interfaces/IssueRepository';
@@ -76,7 +75,6 @@ export class NotifySilentLiveSessionsUseCase {
76
75
  private readonly ownerCallStatusProvider: OwnerCallStatusProvider,
77
76
  private readonly notificationRepository: SilentSessionNotificationRepository,
78
77
  private readonly candidateStateRepository: SilentSessionCandidateStateRepository,
79
- private readonly notifiedStateRepository: SilentSessionNotifiedStateRepository,
80
78
  private readonly messageComposer: SilentSessionMessageComposer,
81
79
  private readonly sleeper: Sleeper,
82
80
  private readonly hubTaskStatusResolver: HubTaskStatusResolver | null = null,
@@ -194,30 +192,8 @@ export class NotifySilentLiveSessionsUseCase {
194
192
  `Silent live session notification: ${debouncedCandidates.length} debounced candidate(s) of ${candidates.length} current candidate(s) across ${interactiveSessions.length} interactive session(s); ${suppressedFirstCycleCount} first-cycle candidate(s) deferred until they persist into the next cycle.`,
195
193
  );
196
194
 
197
- // Fire-once latch: the set of sessions already reminded during their
198
- // current silent episode. A session that is still a candidate this cycle
199
- // and was already reminded is NOT re-injected, so a continuous silent
200
- // episode produces exactly one reminder instead of one every schedule
201
- // cycle. The latch is keyed by the globally-unique session name and
202
- // persisted across the per-cycle fresh monitor process on disk.
203
- const currentCandidateSessionNames = new Set(
204
- candidates.map((candidate) => candidate.sessionName),
205
- );
206
- const previouslyNotifiedSessionNames =
207
- await this.notifiedStateRepository.loadRecentNotifiedSessionNames({
208
- now: params.now,
209
- recencyWindowSeconds: params.candidateDebounceRecencyWindowSeconds,
210
- });
211
-
212
195
  let sentCount = 0;
213
- const notifiedThisCycleSessionNames: string[] = [];
214
196
  for (const candidate of debouncedCandidates) {
215
- if (previouslyNotifiedSessionNames.has(candidate.sessionName)) {
216
- console.log(
217
- `Skipping ${candidate.sessionName}: the current silent-episode reminder was already delivered; not re-injecting until the condition resolves and re-arises.`,
218
- );
219
- continue;
220
- }
221
197
  if (
222
198
  !(await this.isHubTaskActive(
223
199
  candidate.sessionName,
@@ -236,7 +212,6 @@ export class NotifySilentLiveSessionsUseCase {
236
212
  candidate.message,
237
213
  );
238
214
  sentCount += 1;
239
- notifiedThisCycleSessionNames.push(candidate.sessionName);
240
215
  // One line per send, grep-stable on the `Notified ` prefix: the
241
216
  // ISO-8601 UTC timestamp disambiguates concurrent schedule runs and
242
217
  // the section list records what the message actually contained.
@@ -244,25 +219,6 @@ export class NotifySilentLiveSessionsUseCase {
244
219
  `Notified ${candidate.sessionName} at=${params.now.toISOString()} sections=[${candidate.sectionLabels.join(',')}]`,
245
220
  );
246
221
  }
247
-
248
- // Persist the latch for the next cycle: keep every already-latched session
249
- // that is STILL a candidate (refreshing its timestamp so a continuous
250
- // episode stays latched) plus the sessions notified this cycle, and prune
251
- // any latched session that is no longer a candidate so its episode ends and
252
- // a later re-qualification fires a fresh reminder.
253
- const retainedNotifiedSessionNames = [
254
- ...previouslyNotifiedSessionNames,
255
- ].filter((sessionName) => currentCandidateSessionNames.has(sessionName));
256
- const notifiedSessionNamesToPersist = Array.from(
257
- new Set([
258
- ...retainedNotifiedSessionNames,
259
- ...notifiedThisCycleSessionNames,
260
- ]),
261
- );
262
- await this.notifiedStateRepository.saveNotifiedSessionNames({
263
- sessionNames: notifiedSessionNamesToPersist,
264
- now: params.now,
265
- });
266
222
  };
267
223
 
268
224
  private isHubTaskActive = async (
@@ -1 +1 @@
1
- {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAyCA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAkD3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,EACjB,6BAA4B,MAAM,EAAE,GAAG,IAAW,KACjD,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CA8qBP;CACH"}
1
+ {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAyCA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAkD3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,EACjB,6BAA4B,MAAM,EAAE,GAAG,IAAW,KACjD,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAyqBP;CACH"}
@@ -18,7 +18,6 @@ export type NotifySilentTmuxSessionsParams = {
18
18
  staggerSeconds: number;
19
19
  candidateDebounceRecencyWindowSeconds: number;
20
20
  candidateDebounceStateFilePath: string | null;
21
- notifiedStateFilePath: string | null;
22
21
  activeHubTaskStatus: string | null;
23
22
  hubTaskStatusResolver: HubTaskStatusResolver | null;
24
23
  hubTaskStatusCacheStateFilePath: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,EAQtB,MAAM,0DAA0D,CAAC;AAgBlE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAMrE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,0BAA0B,EAAE,MAAM,CAAC;IACnC,+BAA+B,EAAE,MAAM,CAAC;IACxC,8BAA8B,EAAE,MAAM,CAAC;IACvC,+BAA+B,EAAE,MAAM,CAAC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC,EAAE,MAAM,CAAC;IAC9C,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACpD,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,CAAC;IACrC,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAuCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CAmFd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;;;CAS7C,CAAC"}
1
+ {"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,EAQtB,MAAM,0DAA0D,CAAC;AAgBlE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAKrE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,0BAA0B,EAAE,MAAM,CAAC;IACnC,+BAA+B,EAAE,MAAM,CAAC;IACxC,8BAA8B,EAAE,MAAM,CAAC;IACvC,+BAA+B,EAAE,MAAM,CAAC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC,EAAE,MAAM,CAAC;IAC9C,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACpD,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,CAAC;IACrC,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAuCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CA6Ed,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;;;CAS7C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"TmuxSilentSessionNotificationRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,8EAA8E,CAAC;AACnI,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AAKjG,qBAAa,uCAAwC,YAAW,mCAAmC;IACrF,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,yBAAyB,GACvB,aAAa,MAAM,EACnB,SAAS,MAAM,KACd,OAAO,CAAC,IAAI,CAAC,CA6Bd;CACH"}
1
+ {"version":3,"file":"TmuxSilentSessionNotificationRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,8EAA8E,CAAC;AACnI,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AAKjG,qBAAa,uCAAwC,YAAW,mCAAmC;IACrF,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,yBAAyB,GACvB,aAAa,MAAM,EACnB,SAAS,MAAM,KACd,OAAO,CAAC,IAAI,CAAC,CA8Bd;CACH"}
@@ -7,7 +7,6 @@ import { SessionSubAgentActivityRepository } from './adapter-interfaces/SessionS
7
7
  import { SilentSessionMessageComposer } from './adapter-interfaces/SilentSessionMessageComposer';
8
8
  import { SilentSessionNotificationRepository } from './adapter-interfaces/SilentSessionNotificationRepository';
9
9
  import { SilentSessionCandidateStateRepository } from './adapter-interfaces/SilentSessionCandidateStateRepository';
10
- import { SilentSessionNotifiedStateRepository } from './adapter-interfaces/SilentSessionNotifiedStateRepository';
11
10
  import { SilentSessionHubTaskStatusCacheRepository } from './adapter-interfaces/SilentSessionHubTaskStatusCacheRepository';
12
11
  import { Sleeper } from './adapter-interfaces/Sleeper';
13
12
  import { IssueRepository } from './adapter-interfaces/IssueRepository';
@@ -29,14 +28,13 @@ export declare class NotifySilentLiveSessionsUseCase {
29
28
  private readonly ownerCallStatusProvider;
30
29
  private readonly notificationRepository;
31
30
  private readonly candidateStateRepository;
32
- private readonly notifiedStateRepository;
33
31
  private readonly messageComposer;
34
32
  private readonly sleeper;
35
33
  private readonly hubTaskStatusResolver;
36
34
  private readonly hubTaskStatusCacheRepository;
37
35
  private readonly refusalTailStatusProvider;
38
36
  private readonly resolveInteractiveLiveSessions;
39
- constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, candidateStateRepository: SilentSessionCandidateStateRepository, notifiedStateRepository: SilentSessionNotifiedStateRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper, hubTaskStatusResolver?: HubTaskStatusResolver | null, hubTaskStatusCacheRepository?: SilentSessionHubTaskStatusCacheRepository | null, refusalTailStatusProvider?: RefusalTailStatusProvider | null);
37
+ constructor(liveSessionProcessSnapshotProvider: LiveSessionProcessSnapshotProvider, interactiveLiveSessionTranscriptResolver: InteractiveLiveSessionTranscriptResolver, sessionOutputActivityRepository: SessionOutputActivityRepository, subAgentActivityRepository: SessionSubAgentActivityRepository, ownerCallStatusProvider: OwnerCallStatusProvider, notificationRepository: SilentSessionNotificationRepository, candidateStateRepository: SilentSessionCandidateStateRepository, messageComposer: SilentSessionMessageComposer, sleeper: Sleeper, hubTaskStatusResolver?: HubTaskStatusResolver | null, hubTaskStatusCacheRepository?: SilentSessionHubTaskStatusCacheRepository | null, refusalTailStatusProvider?: RefusalTailStatusProvider | null);
40
38
  run: (params: {
41
39
  mainSilentThresholdSeconds: number;
42
40
  unansweredOwnerCallGraceSeconds: number;
@@ -1 +1 @@
1
- {"version":3,"file":"NotifySilentLiveSessionsUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifySilentLiveSessionsUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NotifySilentLiveSessionsUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifySilentLiveSessionsUseCase.ts"],"names":[],"mappings":"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"}