From 526482809190398773a755809be1575e7a32abba Mon Sep 17 00:00:00 2001 From: iris Date: Wed, 1 Jul 2026 18:31:32 +0200 Subject: [PATCH] fix: view-queue link + agent inbox shows unread messages only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(dashboard): update 'view queue' link to /builds.html The build queue moved to its own /builds.html page. The queue-summary 'view queue →' link in tabs.js still pointed at /core.html, so clicking it landed on the wrong page. fix(broker): filter agent inbox to unread (acked_at IS NULL) recent_for was returning all messages regardless of ack state, so the agent inbox showed everything even after 'mark all read'. Now filters to acked_at IS NULL — mirroring exactly what mark_all_read drains — so the inbox empties on reload after the operator drains it. --- frontend/packages/agent/src/app.js | 8 +++----- frontend/packages/dashboard/src/tabs.js | 2 +- hive-c0re/src/broker.rs | 7 ++++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/packages/agent/src/app.js b/frontend/packages/agent/src/app.js index 12a2085a..1451570f 100644 --- a/frontend/packages/agent/src/app.js +++ b/frontend/packages/agent/src/app.js @@ -1133,11 +1133,9 @@ window.marked = marked; return wrap; } // "mark all read" header row drains the host broker's pending + - // delivered-unacked rows for this agent. Visible rows here are - // the most-recent-N regardless of ack state, so the list itself - // doesn't visually empty on click — the status pill confirms - // the drain count, and the next turn_start's "unread" badge - // will read zero. + // delivered-unacked rows for this agent. The inbox shows only + // unread (acked_at IS NULL) rows, so after the drain + refreshState() + // the list empties (matching the operator's expectation). wrap.append(buildInboxMarkAllRow(currentLabel, () => { // Refresh state so any UI surface that DOES depend on // delivery state (eg future per-status filters) picks up diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js index 1da887f3..a08520ea 100644 --- a/frontend/packages/dashboard/src/tabs.js +++ b/frontend/packages/dashboard/src/tabs.js @@ -826,7 +826,7 @@ window.marked = marked; el('span', { class: 'glyph spinner' }, '◐'), ' ', el('strong', {}, 'build queue'), ' — ', parts.join(' · '), ' ', - el('a', { class: 'queue-summary-link', href: '/core.html' }, 'view queue →'), + el('a', { class: 'queue-summary-link', href: '/builds.html' }, 'view queue →'), )); } diff --git a/hive-c0re/src/broker.rs b/hive-c0re/src/broker.rs index be3c9b42..754b1fb3 100644 --- a/hive-c0re/src/broker.rs +++ b/hive-c0re/src/broker.rs @@ -339,9 +339,9 @@ impl Broker { Ok(batch) } - /// Latest `limit` messages addressed to `recipient`, newest-first. - /// Includes delivered + undelivered alike — used for the operator - /// inbox view on the dashboard. Caller decides what to show. + /// Unread (unacked) messages addressed to `recipient`, newest-first. + /// Filters to `acked_at IS NULL` so the agent inbox view clears after + /// "mark all read" — mirroring exactly what `mark_all_read` will drain. pub fn recent_for(&self, recipient: &str, limit: u64) -> Result> { let conn = self.conn.lock().unwrap(); let limit_i = i64::try_from(limit.min(i64::MAX as u64)).unwrap_or(i64::MAX); @@ -349,6 +349,7 @@ impl Broker { "SELECT id, sender, body, sent_at, in_reply_to FROM messages WHERE recipient = ?1 + AND acked_at IS NULL ORDER BY id DESC LIMIT ?2", )?;