fix(approvals): merge-time fixes for the watching badge (#473)

- session.ts: a pane capture that fails now CLEARS the watching label
  (and emits watchingChanged so pages drop the badge) instead of keeping
  the last one, so a failed capture degrades toward an alert rather than
  pre-acknowledging the next real idle prompt. Test updated; invariant
  noted in architecture-invariants.
- approvals-ui.js: the header bell counts only unacknowledged items
  (pendingApprovalsCount), matching codeman tui's pendingApprovalCount();
  pinned in watching-no-alert.test.ts.
- mobile-overview.js: move the orphaned "Pill copy per state" JSDoc back
  onto MOBILE_OVERVIEW_PILL_LABEL.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-09-23 11:40:03 +02:00
parent da6fa663e7
commit 0462a5d5a0
6 changed files with 40 additions and 13 deletions
+9 -6
View File
@@ -2991,15 +2991,18 @@ export class Session extends EventEmitter {
* capture at exactly the moment the turn ends, which is the moment the answer starts
* mattering.
*
* A capture that could not be read leaves the last answer standing, the way the
* working probe treats its own null: no evidence is not evidence of none.
* A capture that could not be read CLEARS the label rather than keeping the last one.
* The two wrong answers are not symmetric: a stale label opens the next idle prompt
* already acknowledged, so a failed capture would silence a real alert, while a dropped
* label only costs a card and an alert that the next readable capture takes back.
* Degrading toward the alert is the rule the whole signal is built on.
*/
private _readWatching(paneText: string | null): void {
const pattern = this._watchingLinePattern();
// Called only from the probe, and only with what a capture returned: `null` is
// "the screen could not be read", which is not evidence that nothing is running.
if (!pattern || paneText === null) return;
const label = watchingLabel(paneText, pattern, this._watchingWindow);
if (!pattern) return;
// `null` is "the screen could not be read". That is no evidence either way, so the
// label falls to null (and the change is announced below like any other).
const label = paneText === null ? null : watchingLabel(paneText, pattern, this._watchingWindow);
if (label === this._watching) return;
this._watching = label;
// ⚠️ This CHANGES while the session's status does not, so it needs an event of its