Skip to content

feat(comments): DELETE /api/comments/{id} admin endpoint - #269

Merged
ajianaz merged 2 commits into
developfrom
feat/comments-delete-endpoint
Sep 11, 2026
Merged

ajianaz merged 2 commits into
developfrom
feat/comments-delete-endpoint

Conversation

@ajianaz

@ajianaz ajianaz commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What

Add DELETE /api/comments/{id} — admin cleanup endpoint that removes a comment row from the local store (204 on success, 404 COMMENT_NOT_FOUND when absent). Includes Store::delete_comment() and OpenAPI registration.

Why

Comment rows could only be removed by direct SQL against the production SQLite file — the comments routes exposed no delete (only list/fetch/sentiment/PATCH reply status). That made every dirty-row cleanup (legacy duplicates, misattributed fetches, test residue) a manual host operation; the production host is not SSH-reachable from agent machines, so the 2026-09-11 duplicate-row cleanup required a hand-run sqlite3 DELETE on the host by the operator.

Closes #267.

How

  • Store::delete_comment() maps rows_affected == 0 to the existing CommentNotFound error (same pattern as get_comment).
  • DB-local by design: the route never calls the Threads API. Deleting someone else's comment on Threads is not possible via the Graph API anyway; on-platform moderation stays with the existing hide/unhide endpoints.
  • Wired as .delete() on the existing /api/comments/{id} route (alongside the PATCH reply-status handler) and registered in utoipa/OpenAPI.

Testing

  • cargo test --workspace passes — all suites green (store_comments 9/9 incl. 2 new, api_comments 2/2 new)
  • cargo fmt --all -- --check passes
  • cargo clippy --workspace --all-targets -- -D warnings passes
  • Cora review — run at PR level (CI "Cora Review")
  • Manual smoke-test: route tests cover 204 + row removal from store, and 404 + COMMENT_NOT_FOUND code for unknown ids; verified no network dependency (store-layer only)

Related Issues

Closes #267

Checklist

  • Branch name follows convention (feat/)
  • Branch is from develop
  • Commit messages follow Conventional Commits
  • No secrets or credentials committed
  • One logical change per PR (no mixed concerns)

Comment rows could only be removed by direct SQL against the
production SQLite file — the comments routes exposed no delete. That
made every dirty-row cleanup (legacy duplicates, test residue) a
manual host operation.

Add Store::delete_comment() (RowNotFound -> CommentNotFound, same as
get_comment) and a DELETE /api/comments/{id} route returning 204, or
404 COMMENT_NOT_FOUND when absent. DB-local by design: no Threads API
call — deleting someone else's comment is not possible via the Graph
API, moderation there stays with hide/unhide. Registered in OpenAPI.

Closes #267
@github-actions

Copy link
Copy Markdown

🔍 Cora AI Code Review

✅ No issues found. Code looks good!


Review powered by cora-code · BYOK · MIT

Resolve conflicts with #268 (cleanup_superseded_comments + its tests)
alongside delete_comment: both methods and both test blocks are kept;
missing closing braces at the splice points restored; fmt/clippy/tests
re-validated on the merged tree.
@ajianaz

ajianaz commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Cora (local pre-commit) raised one [MAJOR] on the merged tree worth answering with evidence:

Cleanup can delete distinct anonymous comments that coincidentally share text (store.rs:1685)

This is a by-design tradeoff inherited from #261, not a regression:

  1. Within titen's own model, distinct anonymous same-text comments cannot coexist as rows. The id-less insert_comment branch dedups by (post_id, text, author-both-NULL) — two different users posting identical text collapse into ONE row at insert time. So an id-less row already represents "the text", not "a person".
  2. The mixed id/id-less twin scenario cannot produce a lost distinct comment through normal fetches: if a later fetch returns ids for both same-text comments, entry 1 backfills the legacy row and entry 2 INSERTs as a new attributed row — cleanup touches nothing. The loss case requires Meta to return an id for one same-text comment and omit it for another in the same response, which contradicts the documented comments: threads_comment_id/author never persisted + Meta /replies returns no id/from for third-party commenters #261 behavior (id omission is per-commenter-access, uniform for third-party commenters).
  3. The alternative — keeping every superseded id-less row forever — is exactly the fix(comments): legacy id-less comment rows superseded by attributed twins are never reconciled #266 bug: proven production double-count in sentiment and reply workflows.

If Meta's behavior ever changes, the correct fix is tighter identity upstream (e.g. hashing commenter-visible fields), not retaining unreconcilable rows.

@ajianaz
ajianaz merged commit 14f05d7 into develop Sep 11, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(comments): DELETE /api/comments/{id} admin endpoint

1 participant