diff --git a/engine/app/controllers/coplan/api/v1/comments_controller.rb b/engine/app/controllers/coplan/api/v1/comments_controller.rb index d0b9645b..2291d5c1 100644 --- a/engine/app/controllers/coplan/api/v1/comments_controller.rb +++ b/engine/app/controllers/coplan/api/v1/comments_controller.rb @@ -125,6 +125,15 @@ def destroy ) end + unless thread.anchored? + Broadcaster.replace_to( + @plan, + target: "plan-general-comments", + partial: "coplan/plans/general_comments", + locals: { threads: @plan.comment_threads.with_kept_comments.includes(:comments).order(:created_at) } + ) + end + render json: { comment_id: comment.id, deleted_at: comment.deleted_at } end diff --git a/engine/app/javascript/controllers/coplan/comment_actions_controller.js b/engine/app/javascript/controllers/coplan/comment_actions_controller.js index e1a4f6a4..a1819856 100644 --- a/engine/app/javascript/controllers/coplan/comment_actions_controller.js +++ b/engine/app/javascript/controllers/coplan/comment_actions_controller.js @@ -3,7 +3,7 @@ import { Controller } from "@hotwired/stimulus" // Reveals per-viewer edit and delete actions when this comment // belongs to the signed-in user. Broadcasts render once for all viewers // with no current_user, so the server emits the affordance for every -// human comment and lets each browser decide whether to show it. The +// human or local_agent comment and lets each browser decide whether to show it. The // server still enforces auth on submit — this is UX, not security. export default class extends Controller { static values = { authorId: String, authorType: String } @@ -11,7 +11,7 @@ export default class extends Controller { connect() { const me = document.querySelector("meta[name='coplan-current-user-id']")?.content - this.isMine = this.authorTypeValue === "human" && + this.isMine = ["human", "local_agent"].includes(this.authorTypeValue) && !!me && this.authorIdValue === me if (this.isMine && this.hasDeleteTarget) { diff --git a/engine/app/policies/coplan/comment_policy.rb b/engine/app/policies/coplan/comment_policy.rb index 01a8a3b0..579343f2 100644 --- a/engine/app/policies/coplan/comment_policy.rb +++ b/engine/app/policies/coplan/comment_policy.rb @@ -1,11 +1,11 @@ module CoPlan class CommentPolicy < ApplicationPolicy def update? - delete? && !record.deleted? + record.author_type == "human" && delete? && !record.deleted? end def delete? - record.author_type == "human" && record.author_id == user&.id + user.present? && record.author_type.in?(%w[human local_agent]) && record.author_id == user.id end end end diff --git a/engine/app/views/coplan/agent_instructions/guides/api.text.erb b/engine/app/views/coplan/agent_instructions/guides/api.text.erb index 3d07212e..f684c1f5 100644 --- a/engine/app/views/coplan/agent_instructions/guides/api.text.erb +++ b/engine/app/views/coplan/agent_instructions/guides/api.text.erb @@ -38,7 +38,7 @@ Every endpoint, with the guide that explains it. All paths start with `<%= @base | `GET /api/v1/plans/:id/comments/:thread_id` | Get one thread. | comments | | `POST /api/v1/plans/:id/comments/:thread_id/reply` | Reply: `body_markdown`. | comments | | `PATCH /api/v1/plans/:id/comments/:thread_id/resolve` | Resolve a thread. | comments | -| `DELETE /api/v1/plans/:id/comments/:comment_id/delete` | Delete one of your principal's own comments. Comments that an agent posted cannot be deleted. | comments | +| `DELETE /api/v1/plans/:id/comments/:comment_id/delete` | Delete a human or local-agent comment attributed to your principal's account. | comments | ## References and attachments diff --git a/engine/app/views/coplan/agent_instructions/guides/comments.text.erb b/engine/app/views/coplan/agent_instructions/guides/comments.text.erb index bd751529..4136817c 100644 --- a/engine/app/views/coplan/agent_instructions/guides/comments.text.erb +++ b/engine/app/views/coplan/agent_instructions/guides/comments.text.erb @@ -57,7 +57,14 @@ Comment when you have a question, when an edit is not yours to make, or when you - Copy `anchor_text` exactly from the plan. CoPlan highlights it for readers. Choose a short, unique phrase. If the phrase occurs more than once, add `anchor_occurrence` (1 for the first occurrence). - Leave out `anchor_text` for a comment on the whole plan. - `@username` in a comment notifies that person. Usernames that do not exist stay plain text. -- You cannot delete a comment that you posted. Write it carefully. To correct it, reply in the thread. +- You can delete human and local-agent comments attributed to your principal's account, including comments from another token for that account. +- You cannot delete another account's comments. Agent comments cannot be edited. To correct one, reply in the thread or delete it. + +To delete a comment, use its comment ID, not its thread ID: + +```bash +<%= @curl %> -X DELETE "<%= @base %>/api/v1/plans/$PLAN_ID/comments/$COMMENT_ID/delete" | jq . +``` ## Review a plan diff --git a/engine/app/views/coplan/comments/_comment.html.erb b/engine/app/views/coplan/comments/_comment.html.erb index f9c393f9..208e2df7 100644 --- a/engine/app/views/coplan/comments/_comment.html.erb +++ b/engine/app/views/coplan/comments/_comment.html.erb @@ -1,7 +1,7 @@ <%# Per-viewer affordances (Edit/Delete) are hidden client-side by the comment_actions Stimulus controller — one broadcast is shared with every - `current_user`, so we ship the button on every human comment and let - the browser remove it for non-authors. Server still enforces auth. %> + `current_user`, so we ship actions on human and local_agent comments and + let the browser reveal them for their owner. Server still enforces auth. %>
<% end %>
+ <% end %> + <% if comment.author_type.in?(%w[human local_agent]) %>