diff --git a/src/main/database/messages.ts b/src/main/database/messages.ts index 785209a6..a3df9bb0 100644 --- a/src/main/database/messages.ts +++ b/src/main/database/messages.ts @@ -264,10 +264,13 @@ export function buildMessageMethods( * visibility rules as listAgentMessages (direct/company/incident always, * department when agent belongs, project when assigned/lead). * - * "Unread" means: - * - Direct messages to this agent: `agent_messages.read_at IS NULL`. - * - Channel messages: no matching row in `agent_message_reads` for - * (message_id, agent_id). + * "Unread" is defined uniformly across every channel: no row in + * `agent_message_reads` for (message_id, agent_id). The legacy + * `agent_messages.read_at` column is never written anywhere in the + * codebase and must not be used as a read signal — it only appears in + * SELECT lists for wire-shape compatibility and is overwritten from the + * read-receipts table by every caller that surfaces `readAt` to agents + * or the renderer. * * Messages where `from_agent_id = agentId` are excluded (agents don't * inject their own sent messages back into their transcript). @@ -331,12 +334,7 @@ export function buildMessageMethods( ).map((row) => row.messageId), ); - return rows.filter((message) => { - if (message.channel === "direct" && message.toAgentId === agentId) { - return !message.readAt; - } - return !readIds.has(message.id); - }); + return rows.filter((message) => !readIds.has(message.id)); }, }; diff --git a/src/main/prompt-builder/wake-reasons/en.ts b/src/main/prompt-builder/wake-reasons/en.ts index 1e0103fb..9eaaee7d 100644 --- a/src/main/prompt-builder/wake-reasons/en.ts +++ b/src/main/prompt-builder/wake-reasons/en.ts @@ -28,7 +28,7 @@ export const WAKE_REASONS: Record = { "Call GET /api/agents/me/standup to get your full briefing", "Review all your assigned tasks and their statuses", "Take action on the most important item: reviews > unblocking > new work", - "If nothing needs attention, post a brief status update on your current task", + "If nothing actionable is in your briefing, return control without posting a status comment — activity noise is worse than silence.", ], }, manual: {