fj pr comment is write-once: no edit/delete, no --edit-last/--create-if-none, unlike fj issue #120

Open
opened 2026-06-11 00:47:20 +00:00 by stephen · 1 comment
Owner

Observation

fj pr can add a comment to a pull request but cannot edit or delete one, and offers no idempotent-upsert flag. fj issue does all three. The PR comment surface is write-once.

fj pr's verb set (src/cli/pr.rs:27-66) has exactly one comment verb:

$ fj pr comment --help
Add a comment to a pull request
Usage: fj pr comment [OPTIONS] <NUMBER>
  -b, --body <BODY>  Comment body. Use `-` to read from stdin. Omit to open `$EDITOR`

CommentArgs (src/cli/pr.rs:234) carries only number + body; the handler (src/cli/pr.rs:592) only posts. There is no EditComment/DeleteComment in the enum, and no --edit-last / --create-if-none.

The issue side already has the full set (src/cli/issue.rs:40-44):

Comment(CommentArgs),
EditComment(EditCommentArgs),
DeleteComment(DeleteCommentArgs),

backed by src/cli/issue_comment.rs (comment / edit_comment / delete_comment).

The decisive part: the write endpoints already exist and already work for PRs. In Forgejo a pull request is an issue, and comment mutation goes through the shared /repos/{owner}/{repo}/issues/comments/{id} path. The helpers api::issue::edit_comment (src/api/issue.rs:216) and api::issue::delete_comment (src/api/issue.rs:232) take a bare comment_id and are not issue-specific at all. So fj pr comment edit/delete could call the exact functions fj issue comment edit/delete already call. The gap is purely a missing CLI verb on the PR side.

Why it matters

The canonical agent/CI pattern is a single maintained status comment: a bot posts "build running...", then edits that same comment to "build green ✓" on completion, rather than spamming a new comment per run. gh pr comment --edit-last --create-if-none --body ... is the standard idempotent primitive for exactly this, and gh issue/pr comment --delete-last cleans up.

With fj today:

  • A bot updating a PR status comment cannot edit it (no fj pr comment edit verb at all), so it either spams new comments or drops to fj api with a hand-managed comment id.
  • Even on the issue side, where edit/delete exist, there is no --edit-last/--create-if-none, so a script must first issue view --comments --json, grep for its own marker, extract the id, and branch on whether it exists. That read-modify-write dance is precisely what --edit-last/--create-if-none collapse into one call.

This is jarring next to the existing surface: fj pr review and fj pr request-review sit right beside fj pr comment, so a user reasonably expects fj pr comment edit to exist and only discovers otherwise mid-script.

Possible directions (sketches)

  • (sketch) Add fj pr comment subverbs (or sibling commands) edit <comment-id> and delete <comment-id> that call the already-present api::issue::edit_comment / delete_comment (src/api/issue.rs:216-232). No new API code; mirror src/cli/issue_comment.rs verbatim. Comment ids come from fj pr view --comments --json.
  • (sketch) Add --edit-last and --create-if-none to both fj issue comment and fj pr comment: list the authenticated user's comments on the target, pick the most recent authored one, and edit it (or create when none exists). This is the gh-parity idempotency flag and the highest-leverage piece for bots.
  • (sketch) If a unified verb is preferred, a single fj comment add/edit/delete <number> working for both issues and PRs (same endpoints) would close the asymmetry in one place.
## Observation `fj pr` can add a comment to a pull request but cannot edit or delete one, and offers no idempotent-upsert flag. `fj issue` does all three. The PR comment surface is write-once. `fj pr`'s verb set (`src/cli/pr.rs:27-66`) has exactly one comment verb: ``` $ fj pr comment --help Add a comment to a pull request Usage: fj pr comment [OPTIONS] <NUMBER> -b, --body <BODY> Comment body. Use `-` to read from stdin. Omit to open `$EDITOR` ``` `CommentArgs` (`src/cli/pr.rs:234`) carries only `number` + `body`; the handler (`src/cli/pr.rs:592`) only posts. There is no `EditComment`/`DeleteComment` in the enum, and no `--edit-last` / `--create-if-none`. The issue side already has the full set (`src/cli/issue.rs:40-44`): ```rust Comment(CommentArgs), EditComment(EditCommentArgs), DeleteComment(DeleteCommentArgs), ``` backed by `src/cli/issue_comment.rs` (`comment` / `edit_comment` / `delete_comment`). The decisive part: the write endpoints already exist and already work for PRs. In Forgejo a pull request *is* an issue, and comment mutation goes through the shared `/repos/{owner}/{repo}/issues/comments/{id}` path. The helpers `api::issue::edit_comment` (`src/api/issue.rs:216`) and `api::issue::delete_comment` (`src/api/issue.rs:232`) take a bare `comment_id` and are not issue-specific at all. So `fj pr comment edit`/`delete` could call the exact functions `fj issue comment edit`/`delete` already call. The gap is purely a missing CLI verb on the PR side. ## Why it matters The canonical agent/CI pattern is a *single maintained status comment*: a bot posts "build running...", then edits that same comment to "build green ✓" on completion, rather than spamming a new comment per run. `gh pr comment --edit-last --create-if-none --body ...` is the standard idempotent primitive for exactly this, and `gh issue/pr comment --delete-last` cleans up. With `fj` today: - A bot updating a PR status comment cannot edit it (no `fj pr comment` edit verb at all), so it either spams new comments or drops to `fj api` with a hand-managed comment id. - Even on the issue side, where edit/delete exist, there is no `--edit-last`/`--create-if-none`, so a script must first `issue view --comments --json`, grep for its own marker, extract the id, and branch on whether it exists. That read-modify-write dance is precisely what `--edit-last`/`--create-if-none` collapse into one call. This is jarring next to the existing surface: `fj pr review` and `fj pr request-review` sit right beside `fj pr comment`, so a user reasonably expects `fj pr comment edit` to exist and only discovers otherwise mid-script. ## Possible directions (sketches) - *(sketch)* Add `fj pr comment` subverbs (or sibling commands) `edit <comment-id>` and `delete <comment-id>` that call the already-present `api::issue::edit_comment` / `delete_comment` (`src/api/issue.rs:216-232`). No new API code; mirror `src/cli/issue_comment.rs` verbatim. Comment ids come from `fj pr view --comments --json`. - *(sketch)* Add `--edit-last` and `--create-if-none` to both `fj issue comment` and `fj pr comment`: list the authenticated user's comments on the target, pick the most recent authored one, and edit it (or create when none exists). This is the gh-parity idempotency flag and the highest-leverage piece for bots. - *(sketch)* If a unified verb is preferred, a single `fj comment add/edit/delete <number>` working for both issues and PRs (same endpoints) would close the asymmetry in one place.
Author
Owner

Converted to backlog item rasterstate/fj#131 (p2, size M).

Converted to backlog item rasterstate/fj#131 (p2, size M).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rasterstate/fj#120
No description provided.