diff --git a/engine/app/controllers/coplan/plans_controller.rb b/engine/app/controllers/coplan/plans_controller.rb
index 128f36d..f9dfd6c 100644
--- a/engine/app/controllers/coplan/plans_controller.rb
+++ b/engine/app/controllers/coplan/plans_controller.rb
@@ -630,6 +630,13 @@ def load_needs_attention
@attention_unread_counts = unread_by_plan
top_ids = unread_by_plan.sort_by { |_id, count| -count }
.first(ATTENTION_LIMIT).map(&:first)
+ # The plan view hides resolved threads by default. Route each inbox row
+ # through an unread notification so the destination marks it read and
+ # deep-links to the exact thread, even when that thread is resolved.
+ # This is deliberately bounded to ATTENTION_LIMIT indexed lookups.
+ @attention_notification_ids = top_ids.index_with do |plan_id|
+ current_user.notifications.unread.where(plan_id: plan_id).newest_first.pick(:id)
+ end
# Even an inbox routes through the discovery predicate — a stale
# notification must not resurface an archived plan or another user's
# unlisted draft.
diff --git a/engine/app/views/coplan/plans/index.html.erb b/engine/app/views/coplan/plans/index.html.erb
index 601ebfd..a788d1a 100644
--- a/engine/app/views/coplan/plans/index.html.erb
+++ b/engine/app/views/coplan/plans/index.html.erb
@@ -112,7 +112,7 @@
<% @attention_plans.each do |plan| %>
-
- <%= link_to plan.title, plan_path(plan), class: "attention__link" %>
+ <%= link_to plan.title, notification_path(@attention_notification_ids.fetch(plan.id)), class: "attention__link", data: { turbo: false } %>
<%= pluralize(@attention_unread_counts[plan.id].to_i, "unread comment") %>
<% end %>
diff --git a/spec/requests/plans_spec.rb b/spec/requests/plans_spec.rb
index 5fec026..8d1de8a 100644
--- a/spec/requests/plans_spec.rb
+++ b/spec/requests/plans_spec.rb
@@ -733,11 +733,24 @@
describe "needs attention strip" do
it "lists plans with unread comments for the current user" do
thread = create(:comment_thread, plan: plan, created_by_user: bob)
- create(:notification, user: alice, plan: plan, comment_thread: thread)
+ notification = create(:notification, user: alice, plan: plan, comment_thread: thread)
get plans_path
expect(response.body).to include("Needs attention (1)")
expect(response.body).to include("1 unread comment")
+ attention_link = Nokogiri::HTML(response.body).at_css(".attention__link")
+ expect(attention_link["href"]).to eq(notification_path(notification))
+ expect(attention_link["data-turbo"]).to eq("false")
+ end
+
+ it "links resolved unread comments through their notification deep link" do
+ thread = create(:comment_thread, :resolved, plan: plan, created_by_user: bob)
+ notification = create(:notification, user: alice, plan: plan, comment_thread: thread)
+
+ get plans_path
+
+ expect(response.body).to include(notification_path(notification))
+ expect(response.body).not_to include(%(href="#{plan_path(plan)}" class="attention__link"))
end
it "is omitted when nothing is unread" do