github-issue-tower-defence-management 1.111.1 → 1.111.3

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 +14 -0
  2. package/README.md +1 -1
  3. package/bin/adapter/entry-points/console/ui-dist/assets/{index-CT3t4Yje.js → index-Bu5bQZyP.js} +19 -19
  4. package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
  5. package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js +6 -2
  6. package/bin/adapter/repositories/ConfigurableSilentSessionMessageComposer.js.map +1 -1
  7. package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js +15 -2
  8. package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js.map +1 -1
  9. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js +3 -2
  10. package/bin/domain/usecases/DefaultSilentSessionMessageComposer.js.map +1 -1
  11. package/bin/domain/usecases/silentSessionReminderSentinel.js +12 -0
  12. package/bin/domain/usecases/silentSessionReminderSentinel.js.map +1 -0
  13. package/package.json +1 -1
  14. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +59 -0
  15. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.ts +17 -0
  16. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +79 -0
  17. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +10 -5
  18. package/src/adapter/entry-points/console/ui-dist/assets/{index-CT3t4Yje.js → index-Bu5bQZyP.js} +19 -19
  19. package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
  20. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +4 -1
  21. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +19 -1
  22. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +8 -2
  23. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +67 -0
  24. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +17 -2
  25. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +13 -0
  26. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +3 -2
  27. package/src/domain/usecases/silentSessionReminderSentinel.ts +9 -0
  28. package/types/adapter/repositories/ConfigurableSilentSessionMessageComposer.d.ts.map +1 -1
  29. package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts.map +1 -1
  30. package/types/domain/usecases/DefaultSilentSessionMessageComposer.d.ts.map +1 -1
  31. package/types/domain/usecases/silentSessionReminderSentinel.d.ts +2 -0
  32. package/types/domain/usecases/silentSessionReminderSentinel.d.ts.map +1 -0
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TDPM Console</title>
7
- <script type="module" crossorigin src="/assets/index-CT3t4Yje.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-Bu5bQZyP.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-BPH6W1yn.css">
9
9
  </head>
10
10
  <body>
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConfigurableSilentSessionMessageComposer = void 0;
4
+ const silentSessionReminderSentinel_1 = require("../../domain/usecases/silentSessionReminderSentinel");
5
+ const withReminderSentinel = (message) => message.includes(silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL)
6
+ ? message
7
+ : `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} ${message}`;
4
8
  const formatMinutes = (seconds) => {
5
9
  const minutes = Math.floor(seconds / 60);
6
10
  return `${minutes}m`;
@@ -13,7 +17,7 @@ class ConfigurableSilentSessionMessageComposer {
13
17
  if (this.templates.mainStalledMessage === null) {
14
18
  return this.fallback.composeMainStalledSection(mainSilentSeconds);
15
19
  }
16
- return this.templates.mainStalledMessage;
20
+ return withReminderSentinel(this.templates.mainStalledMessage);
17
21
  };
18
22
  this.composeSubAgentSection = (subAgents) => {
19
23
  if (this.templates.subAgentMessageHeader === null &&
@@ -29,7 +33,7 @@ class ConfigurableSilentSessionMessageComposer {
29
33
  if (this.templates.subAgentMessageFooter !== null) {
30
34
  sections.push(this.templates.subAgentMessageFooter);
31
35
  }
32
- return sections.join('\n');
36
+ return withReminderSentinel(sections.join('\n'));
33
37
  };
34
38
  }
35
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigurableSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AASA,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAa,wCAAwC;IACnD,YACmB,SAAwC,EACxC,QAAsC;QADtC,cAAS,GAAT,SAAS,CAA+B;QACxC,aAAQ,GAAR,QAAQ,CAA8B;QAGzD,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;gBAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;YACpE,CAAC;YACD,OAAO,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;QAC3C,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,IACE,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI;gBAC7C,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAC7C,CAAC;gBACD,OAAO,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;YACxB,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC;IA/BC,CAAC;CAgCL;AApCD,4FAoCC"}
1
+ {"version":3,"file":"ConfigurableSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AAEA,uGAAuG;AAEvG,MAAM,oBAAoB,GAAG,CAAC,OAAe,EAAU,EAAE,CACvD,OAAO,CAAC,QAAQ,CAAC,gEAAgC,CAAC;IAChD,CAAC,CAAC,OAAO;IACT,CAAC,CAAC,GAAG,gEAAgC,IAAI,OAAO,EAAE,CAAC;AAQvD,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAa,wCAAwC;IACnD,YACmB,SAAwC,EACxC,QAAsC;QADtC,cAAS,GAAT,SAAS,CAA+B;QACxC,aAAQ,GAAR,QAAQ,CAA8B;QAGzD,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;gBAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;YACpE,CAAC;YACD,OAAO,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACjE,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,IACE,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI;gBAC7C,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAC7C,CAAC;gBACD,OAAO,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;YACxB,IAAI,IAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,IAAI,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YACtD,CAAC;YACD,OAAO,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC;IA/BC,CAAC;CAgCL;AApCD,4FAoCC"}
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.TranscriptOwnerCallStatusProvider = void 0;
37
37
  const fs = __importStar(require("fs"));
38
+ const silentSessionReminderSentinel_1 = require("../../domain/usecases/silentSessionReminderSentinel");
38
39
  const isRecord = (value) => typeof value === 'object' && value !== null;
39
40
  const readString = (value, key) => {
40
41
  const candidate = value[key];
@@ -67,12 +68,24 @@ const extractText = (content) => {
67
68
  };
68
69
  const hasOwnerTextReply = (content) => {
69
70
  if (typeof content === 'string') {
70
- return content.length > 0;
71
+ if (content.length === 0) {
72
+ return false;
73
+ }
74
+ // A monitor-injected self-check reminder lands in the target session's
75
+ // transcript as a user text entry. It carries the reminder sentinel, so it
76
+ // is the monitor talking to the session, not the owner replying. It MUST NOT
77
+ // advance the last-owner-reply time, otherwise an outstanding call-to-user
78
+ // is wrongly treated as answered and the session stops being suppressed.
79
+ return !content.includes(silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL);
71
80
  }
72
81
  if (!Array.isArray(content)) {
73
82
  return false;
74
83
  }
75
- return content.some((block) => isRecord(block) && block.type === 'text');
84
+ const hasTextBlock = content.some((block) => isRecord(block) && block.type === 'text');
85
+ if (!hasTextBlock) {
86
+ return false;
87
+ }
88
+ return !extractText(content).includes(silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL);
76
89
  };
77
90
  class TranscriptOwnerCallStatusProvider {
78
91
  constructor(ownerCallMarker) {
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptOwnerCallStatusProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TranscriptOwnerCallStatusProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DefaultSilentSessionMessageComposer = void 0;
4
+ const silentSessionReminderSentinel_1 = require("./silentSessionReminderSentinel");
4
5
  const formatMinutes = (seconds) => {
5
6
  const minutes = Math.floor(seconds / 60);
6
7
  return `${minutes}m`;
@@ -8,7 +9,7 @@ const formatMinutes = (seconds) => {
8
9
  const composeMainStalledMessage = (mainSilentSeconds) => {
9
10
  const minutes = Math.floor(mainSilentSeconds / 60);
10
11
  return [
11
- `You have produced no output for ${minutes} minutes. Idle waiting wastes this live session and is unacceptable. Always work proactively and stay ahead of the work: anticipate the next steps and, at every point, choose the fastest path so the whole task finishes as early as possible; never wait passively. Finish every task in the shortest possible time — but "fastest" means correct and incident-free, not merely quick: a fast but wrong result is worthless and causes incidents, so be fast without breaking things. Use parallel execution and your whole team of sub-agents to minimize total wall-clock time. Your goal is to drive every task to completion and have the owner confirm that all tasks are done. Do NOT close this session on your own — it is closed only after the owner has verified completion and tells you to close it. Self-check now:`,
12
+ `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} You have produced no output for ${minutes} minutes. Idle waiting wastes this live session and is unacceptable. Always work proactively and stay ahead of the work: anticipate the next steps and, at every point, choose the fastest path so the whole task finishes as early as possible; never wait passively. Finish every task in the shortest possible time — but "fastest" means correct and incident-free, not merely quick: a fast but wrong result is worthless and causes incidents, so be fast without breaking things. Use parallel execution and your whole team of sub-agents to minimize total wall-clock time. Your goal is to drive every task to completion and have the owner confirm that all tasks are done. Do NOT close this session on your own — it is closed only after the owner has verified completion and tells you to close it. Self-check now:`,
12
13
  `1. Every request from the owner is registered as a session task and your task list is kept current (mark tasks completed when done); verify nothing is missing or stale.`,
13
14
  `2. Your plan is the fastest correct path: parallelize independent work across sub-agents, delegate, and remove needless serialization. Choose the fastest safe method, not the easiest.`,
14
15
  `3. A monitor is in place that detects when any sub-agent produces no output for 5 minutes.`,
@@ -24,7 +25,7 @@ class DefaultSilentSessionMessageComposer {
24
25
  this.composeSubAgentSection = (subAgents) => {
25
26
  const lines = subAgents.map((subAgent) => `- ${subAgent.label}: silent for ${formatMinutes(subAgent.silentSeconds)}, running for ${formatMinutes(subAgent.runningSeconds)}`);
26
27
  return [
27
- 'The following sub-processes have been silent or running for a long time:',
28
+ `${silentSessionReminderSentinel_1.SILENT_SESSION_REMINDER_SENTINEL} The following sub-processes have been silent or running for a long time:`,
28
29
  ...lines,
29
30
  'If a sub-process is stalled, take action (restart, hand off, or replace it). If it is legitimately waiting on an external dependency (continuous integration, an external API, or another process), let it continue.',
30
31
  ].join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AAGA,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;IACnD,OAAO;QACL,mCAAmC,OAAO,syBAAsyB;QACh1B,0KAA0K;QAC1K,yLAAyL;QACzL,4FAA4F;QAC5F,0YAA0Y;QAC1Y,qHAAqH;KACtH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC,CAAC;AAEF,MAAa,mCAAmC;IAAhD;QACE,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,OAAO,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;QACtD,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,OAAO;gBACL,0EAA0E;gBAC1E,GAAG,KAAK;gBACR,sNAAsN;aACvN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC;IACJ,CAAC;CAAA;AAlBD,kFAkBC"}
1
+ {"version":3,"file":"DefaultSilentSessionMessageComposer.js","sourceRoot":"","sources":["../../../src/domain/usecases/DefaultSilentSessionMessageComposer.ts"],"names":[],"mappings":";;;AAEA,mFAAmF;AAEnF,MAAM,aAAa,GAAG,CAAC,OAAe,EAAU,EAAE;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;IACnD,OAAO;QACL,GAAG,gEAAgC,oCAAoC,OAAO,syBAAsyB;QACp3B,0KAA0K;QAC1K,yLAAyL;QACzL,4FAA4F;QAC5F,0YAA0Y;QAC1Y,qHAAqH;KACtH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC,CAAC;AAEF,MAAa,mCAAmC;IAAhD;QACE,8BAAyB,GAAG,CAAC,iBAAyB,EAAU,EAAE;YAChE,OAAO,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;QACtD,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,SAA6B,EAAU,EAAE;YACjE,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CACzB,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,KAAK,gBAAgB,aAAa,CAC9C,QAAQ,CAAC,aAAa,CACvB,iBAAiB,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAC7D,CAAC;YACF,OAAO;gBACL,GAAG,gEAAgC,2EAA2E;gBAC9G,GAAG,KAAK;gBACR,sNAAsN;aACvN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC;IACJ,CAAC;CAAA;AAlBD,kFAkBC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SILENT_SESSION_REMINDER_SENTINEL = void 0;
4
+ // Stable, recognizable marker embedded in every monitor-injected silent-session
5
+ // self-check reminder. It lets owner-reply detection distinguish a reminder that
6
+ // the monitor injected into a session's transcript (which arrives as a user text
7
+ // entry via `tmux send-keys`) from a genuine owner reply, so an injected reminder
8
+ // is never miscounted as the owner answering an outstanding call-to-user. The tag
9
+ // is bracketed and namespaced to make an accidental collision with genuine human
10
+ // owner text effectively impossible.
11
+ exports.SILENT_SESSION_REMINDER_SENTINEL = '[TDPM_SILENT_SESSION_SELF_CHECK_REMINDER]';
12
+ //# sourceMappingURL=silentSessionReminderSentinel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silentSessionReminderSentinel.js","sourceRoot":"","sources":["../../../src/domain/usecases/silentSessionReminderSentinel.ts"],"names":[],"mappings":";;;AAAA,gFAAgF;AAChF,iFAAiF;AACjF,iFAAiF;AACjF,kFAAkF;AAClF,kFAAkF;AAClF,iFAAiF;AACjF,qCAAqC;AACxB,QAAA,gCAAgC,GAC3C,2CAA2C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.111.1",
3
+ "version": "1.111.3",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  countPendingItems,
3
+ countTabPendingItems,
3
4
  filterPendingItems,
5
+ filterTabPendingItems,
4
6
  isOverlayEntryActed,
5
7
  overlayKeyForItem,
6
8
  overlayStorageKey,
9
+ tabIgnoresDoneOverlay,
7
10
  writeOverlayEntry,
8
11
  } from './overlay';
9
12
  import type { ConsoleListItem, ConsoleOverlay } from './types';
@@ -123,6 +126,62 @@ describe('filterPendingItems', () => {
123
126
  });
124
127
  });
125
128
 
129
+ describe('tabIgnoresDoneOverlay', () => {
130
+ it('ignores the done overlay on the workflow-blocker tab', () => {
131
+ expect(tabIgnoresDoneOverlay('workflow-blocker')).toBe(true);
132
+ });
133
+
134
+ it('applies the done overlay on every other tab', () => {
135
+ expect(tabIgnoresDoneOverlay('prs')).toBe(false);
136
+ expect(tabIgnoresDoneOverlay('triage')).toBe(false);
137
+ expect(tabIgnoresDoneOverlay('unread')).toBe(false);
138
+ expect(tabIgnoresDoneOverlay('failed-preparation')).toBe(false);
139
+ expect(tabIgnoresDoneOverlay('todo-by-human')).toBe(false);
140
+ });
141
+ });
142
+
143
+ describe('countTabPendingItems', () => {
144
+ it('counts every workflow-blocker item even when all are marked done', () => {
145
+ const overlay: ConsoleOverlay = {
146
+ PVTI_1: { ts: 100, mode: 'workflow-blocker', done: true },
147
+ PVTI_2: { ts: 100, mode: 'workflow-blocker', done: true },
148
+ };
149
+ expect(
150
+ countTabPendingItems([item(1), item(2)], overlay, 'workflow-blocker'),
151
+ ).toBe(2);
152
+ });
153
+
154
+ it('subtracts done items on tabs other than workflow-blocker', () => {
155
+ const overlay: ConsoleOverlay = {
156
+ PVTI_1: { ts: 100, mode: 'prs', done: true },
157
+ };
158
+ expect(countTabPendingItems([item(1), item(2)], overlay, 'prs')).toBe(1);
159
+ });
160
+ });
161
+
162
+ describe('filterTabPendingItems', () => {
163
+ it('keeps every workflow-blocker item even when all are marked done', () => {
164
+ const overlay: ConsoleOverlay = {
165
+ PVTI_1: { ts: 100, mode: 'workflow-blocker', done: true },
166
+ PVTI_2: { ts: 100, mode: 'workflow-blocker', done: true },
167
+ };
168
+ const result = filterTabPendingItems(
169
+ [item(1), item(2)],
170
+ overlay,
171
+ 'workflow-blocker',
172
+ );
173
+ expect(result.map((entry) => entry.number)).toEqual([1, 2]);
174
+ });
175
+
176
+ it('drops done items on tabs other than workflow-blocker', () => {
177
+ const overlay: ConsoleOverlay = {
178
+ PVTI_1: { ts: 100, mode: 'prs', done: true },
179
+ };
180
+ const result = filterTabPendingItems([item(1), item(2)], overlay, 'prs');
181
+ expect(result.map((entry) => entry.number)).toEqual([2]);
182
+ });
183
+ });
184
+
126
185
  describe('writeOverlayEntry', () => {
127
186
  it('stamps the timestamp and mode on write', () => {
128
187
  const next = writeOverlayEntry({}, 'PVTI_1', { done: true }, 'prs', 1234);
@@ -30,6 +30,23 @@ export const filterPendingItems = (
30
30
  (item) => !isOverlayEntryActed(overlay[overlayKeyForItem(item)]),
31
31
  );
32
32
 
33
+ export const tabIgnoresDoneOverlay = (tab: ConsoleTabName): boolean =>
34
+ tab === 'workflow-blocker';
35
+
36
+ export const countTabPendingItems = (
37
+ items: ConsoleListItem[],
38
+ overlay: ConsoleOverlay,
39
+ tab: ConsoleTabName,
40
+ ): number =>
41
+ tabIgnoresDoneOverlay(tab) ? items.length : countPendingItems(items, overlay);
42
+
43
+ export const filterTabPendingItems = (
44
+ items: ConsoleListItem[],
45
+ overlay: ConsoleOverlay,
46
+ tab: ConsoleTabName,
47
+ ): ConsoleListItem[] =>
48
+ tabIgnoresDoneOverlay(tab) ? items : filterPendingItems(items, overlay);
49
+
33
50
  export const writeOverlayEntry = (
34
51
  overlay: ConsoleOverlay,
35
52
  key: string,
@@ -255,6 +255,85 @@ describe('ConsolePage', () => {
255
255
  expect(queryByText('project: umino')).toBeNull();
256
256
  });
257
257
 
258
+ it('keeps the workflow-blocker tab visible and lists its items even when every item is marked done in the overlay', async () => {
259
+ const blockerItems = [
260
+ {
261
+ number: 701,
262
+ title: 'Blocked deployment task',
263
+ url: 'https://github.com/o/r/issues/701',
264
+ repo: 'o/r',
265
+ nameWithOwner: 'o/r',
266
+ projectItemId: 'PVTI_B1',
267
+ itemId: 'PVTI_B1',
268
+ isPr: false,
269
+ story: 'TDPM Console port',
270
+ status: 'In Progress',
271
+ nextActionDate: null,
272
+ nextActionHour: null,
273
+ dependedIssueUrls: [],
274
+ labels: [],
275
+ createdAt: '2026-06-17T00:00:00.000Z',
276
+ },
277
+ {
278
+ number: 702,
279
+ title: 'Blocked rollout task',
280
+ url: 'https://github.com/o/r/issues/702',
281
+ repo: 'o/r',
282
+ nameWithOwner: 'o/r',
283
+ projectItemId: 'PVTI_B2',
284
+ itemId: 'PVTI_B2',
285
+ isPr: false,
286
+ story: 'TDPM Console port',
287
+ status: 'In Progress',
288
+ nextActionDate: null,
289
+ nextActionHour: null,
290
+ dependedIssueUrls: [],
291
+ labels: [],
292
+ createdAt: '2026-06-17T01:00:00.000Z',
293
+ },
294
+ ];
295
+ const fetchMock = jest.fn(async (url: string) => {
296
+ const listMatch = url.match(/\/projects\/[^/]+\/([^/]+)\/list\.json/);
297
+ if (listMatch !== null) {
298
+ return {
299
+ ok: true,
300
+ status: 200,
301
+ json: async () =>
302
+ listMatch[1] === 'workflow-blocker'
303
+ ? { ...listPayload('workflow-blocker'), items: blockerItems }
304
+ : listPayload(listMatch[1]),
305
+ };
306
+ }
307
+ return { ok: true, status: 200, json: async () => ({ body: '# body' }) };
308
+ });
309
+ global.fetch = fetchMock as unknown as typeof fetch;
310
+ localStorage.setItem(
311
+ 'pv_overlay_umino',
312
+ JSON.stringify({
313
+ PVTI_B1: { ts: 1, mode: 'workflow-blocker', done: true },
314
+ PVTI_B2: { ts: 1, mode: 'workflow-blocker', done: true },
315
+ }),
316
+ );
317
+ window.history.replaceState(
318
+ {},
319
+ '',
320
+ '/projects/umino/workflow-blocker?k=token',
321
+ );
322
+
323
+ const { getByText } = render(<ConsolePage />);
324
+ await waitFor(() => {
325
+ expect(getByText('Blocked deployment task')).toBeInTheDocument();
326
+ });
327
+ expect(getByText('Blocked rollout task')).toBeInTheDocument();
328
+ const blockerTab = within(tabBar())
329
+ .getByText('Workflow Blocker')
330
+ .closest('a');
331
+ expect(blockerTab).not.toBeNull();
332
+ expect(blockerTab?.querySelector('.console-tab-badge')?.textContent).toBe(
333
+ '2',
334
+ );
335
+ });
336
+
258
337
  it('renders the failure toast in English without any Japanese characters', async () => {
259
338
  const fetchMock = jest.fn(
260
339
  async (url: string, init?: { method?: string }) => {
@@ -25,8 +25,8 @@ import {
25
25
  previousPendingKeyBefore,
26
26
  } from '../logic/navigation';
27
27
  import {
28
- countPendingItems,
29
- filterPendingItems,
28
+ countTabPendingItems,
29
+ filterTabPendingItems,
30
30
  overlayKeyForItem,
31
31
  } from '../logic/overlay';
32
32
  import type { ConsoleSwipeDirection } from '../logic/swipe';
@@ -64,9 +64,10 @@ export const ConsolePage = () => {
64
64
  if (snapshot === null) {
65
65
  continue;
66
66
  }
67
- result[tab.name] = countPendingItems(
67
+ result[tab.name] = countTabPendingItems(
68
68
  snapshot.items,
69
69
  overlayState.overlay,
70
+ tab.name,
70
71
  );
71
72
  }
72
73
  return result;
@@ -86,8 +87,12 @@ export const ConsolePage = () => {
86
87
  if (activeSnapshot === null) {
87
88
  return [];
88
89
  }
89
- return filterPendingItems(activeSnapshot.items, overlayState.overlay);
90
- }, [activeSnapshot, overlayState.overlay]);
90
+ return filterTabPendingItems(
91
+ activeSnapshot.items,
92
+ overlayState.overlay,
93
+ activeTab,
94
+ );
95
+ }, [activeSnapshot, overlayState.overlay, activeTab]);
91
96
 
92
97
  const orderedPendingKeys = useMemo(
93
98
  () => pendingItems.map((item) => overlayKeyForItem(item)),