The attention list, exactly

Every row the Dashboard can show, rendered rather than described. Grouped by how the row clears, because that is the thing the current screen hides: some rows fix themselves and some sit until dismissed, and today they look identical. Text and links are transcribed from home.service.ts and red-rules.ts, not written for this page.

Built ships today, exactly as drawn
On the wire the data exists, the UI does not use it
Proposed does not exist yet

Group 1 · Not working

Computed fresh on every load from live database state. Nobody dismisses these — they disappear when the thing is fixed. So the only CTA is one that takes you to where you fix it. Usually empty, and it looks alarming precisely because it usually is not there. home.service.ts:398-425

The bot cannot write replies at allBuilt The model provider has refused every call since 09:12, five hours ago. Nothing has been answered on any channel since. Open Models
The Marina launch campaign is stuckBuilt It sent 184 of 412 and stopped 20 minutes ago. The remaining 228 people have not been contacted. Open campaign
The Summer offer campaign is failing to sendBuilt 31 sends failed in the last hour. Usually the template, the contact's consent, or the quality rating. Open campaign
The database is unreachableOn the wire Fires as an alert today, but never reaches this list — it has no computed row, so the Dashboard cannot show it even while everything is down. Open Settings
The moderator is letting comments through uncheckedOn the wire 14 comments in the last 30 minutes stayed public because the moderator could not decide. Same gap: an alert exists, a row does not. Open Activity
Defect this catalogue found. The first three rows also exist as notification kinds — red.campaign.stuck, red.campaign.failure_spike, red.llm. One stuck campaign therefore renders twice on today's Home, once self-clearing and once dismissible, side by side saying the same thing. Deduplicating on dedupKey in home.service.ts is the fix, and it is the same fold the fail-safe row already does at line 497.

Group 2 · Waiting for you

Also computed, also self-clearing — but this is routine queue work, not breakage, and it should never look like an emergency. It drains to zero as you work it. Count first, because the count is the decision. home.service.ts:428-509

3comment replies to approve before they sendoldest 3h 20m 2DM replies to mark right or wrong, so the bot learnsoldest 1h 02m 1hidden comment to delete for good, or put backoldest 44m 2Instagram comments still public, the bot's hide failedoldest 12m 1 comment actions that failedoldest 8m 4times the bot chose not to answerlast 24 hours 1review items waiting (a kind nobody has given a sentence yet)oldest 3m

The last row is the real fallback string in the code. It appears whenever a review kind is added without adding its sentence — the screen degrades to engineering vocabulary rather than breaking, which is right, but every kind should get its own line before it ships.

Group 3 · Told you about

Saved messages, written when an alert fired. These are the only rows a person dismisses, and the only ones that survive a restart. They can be stale — the thing may already be fixed and the row still sits here until cleared. red-rules.ts:69-173

Told you about7
WhatsApp webhooks silent for 74hBuilt Threshold is 6h. Check the Meta subscription, the connection, and the server. · 2 days ago Open
Redis is unreachableBuilt Queues for webhooks, campaigns and reminders are not being processed. · 6 hours ago Open
Moderator failing safe: 14 comments left unmoderated in 30mBuilt The moderator could not decide, so those comments stayed public with no review item. · 20 minutes ago Open
Yesterday's summaryBuilt The daily digest. Nothing is wrong; it is a report. · 09:00

The dashed “Open” buttons are the point. alert-evaluator.service.ts:98 already stores data: {href}, and the API already returns the whole row — the link is on the wire right now. The web type just never declared data, so today a critical alert's only affordance is to make it go away. Declaring one field turns every one of these into an actionable row.

Every kind of CTA a row can carry

Two exist. The rest are proposals, each answering a row that currently has no honest action.

NavigateBuilt Go to where the thing is fixed. Every computed row has exactly one. Never mutates anything. Open Models
Dismiss, and dismiss allBuilt Notifications only. Optimistic — the row goes at once, one quiet retry behind it, and it comes back with a notice if the server refused. “Dismiss all” appears at two or more.
Navigate from a notificationOn the wire Zero backend work. The href is stored and returned; the client type omits it. Open
Resolve hereProposed The queue rows are counts of items that each need one decision. Sending you to a list to click one thing is the long way round. Review 3
Try againProposed The failed-comment rows are failed Graph calls. The honest action for a failed call is to retry it — today it navigates to a list that cannot retry either. The one CTA here that needs a new endpoint. Try again
SnoozeProposed Different from dismiss, because it comes back. A silence alert for a channel you already know about is what trains people to ignore the list. Snooze
Stop telling me thisOn the wire alerting-config.ts already supports seeded muted channels and labels — the WhatsApp mute was applied by editing that config. There is no UI for it anywhere. Mute
UndoProposed A dismissed notification cannot be recovered from the UI. CONVENTIONS §1.4 says undo beats confirm. Undo

When a group is empty

Three groups, three different empties. None of them is a single “all clear”.

Not working — empty is the normal state

Nothing is brokenEverything the bot needs is answering.

Waiting for you — empty means you are done

Your queue is clearNothing is waiting on a decision from you.

Told you about — empty means you cleared it

Nothing newAlerts you have not read yet show up here.

Nothing anywhere, and nothing connected yet

No channel is connectedConnect WhatsApp, Instagram or Facebook and this fills in on its own.