feat(seidroid-review): acknowledge the trigger with a reaction - #72
Conversation
A review takes minutes to reach its first visible output: the Go toolchain, the driver install, and the session start all run before anything appears on the pull request. Until then nothing distinguishes "the trigger was seen" from "the trigger was dropped", and the person who asked has no way to tell which without opening the Actions tab. React to the triggering comment as the first step of the review job, so the acknowledgement precedes the work rather than trailing it. Comment path only: an automatic pull_request review has no comment to react to, so the guard leaves comment_id empty and the step is skipped. It is also continue-on-error -- an acknowledgement is a courtesy, and failing the review because a reaction did not post would trade the job for the signal that the job started. A reaction on a pull-request comment goes to the ISSUE comments endpoint, which `pull-requests: write` does not cover, so the job also takes `issues: write`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR SummaryLow Risk Overview The step runs only when the guard supplies a The job adds Reviewed by Cursor Bugbot for commit a951ebb. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
A small, well-scoped addition that reacts 👀 to the triggering comment as the first step of the review job; the shell is injection-safe (all GitHub values routed through env), the guard correctly skips the automatic pull_request path, and the idempotency claim holds. Two rollout/diagnostic concerns worth addressing before merge, none of which break the same-repo happy path.
Findings: 0 blocking | 4 non-blocking | 3 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- The header block at the top of the file documents every other caller-facing requirement (pins, secrets, trigger wiring) but says nothing about the job's GITHUB_TOKEN scopes. Since this change makes a caller-side permission newly load-bearing, a line in that block — or in
.github/seidroid/README.md— stating that the calling job must grantissues: writewould save the next consumer a failed run. - 3 suggestion(s)/nit(s) flagged inline on specific lines.
| checks: write # publish the review check run | ||
| # React to the triggering comment. A reaction on a PR comment goes to the | ||
| # ISSUE comments endpoint, which pull-requests: write does not cover. | ||
| issues: write # acknowledge the trigger with a reaction |
There was a problem hiding this comment.
[suggestion] This is a reusable workflow, so the caller's token is the ceiling: a called job may only downgrade the caller's permissions, never elevate them. Where the calling job enumerates its permissions: (as every workflow in this repo does) without issues: write — or where the org restricts the default GITHUB_TOKEN — GitHub rejects the request rather than intersecting it, failing the whole review job before any step runs (The workflow is requesting 'issues: write', but is only allowed 'issues: none'). The step's continue-on-error cannot protect against that, because the failure is workflow validation, not the step.
The effect is the inverse of the change's purpose: a caller that bumps the pin without also adding the permission loses the entire review, not just the acknowledgement. Worth confirming sei-load#97 adds issues: write to the caller job and lands with the bump, and worth stating the new caller requirement in this file's header alongside the other caller-facing wiring.
| # the same comment returns the existing reaction rather than adding a | ||
| # second one, so a retry needs no cleanup. | ||
| if gh api -X POST "repos/$REPO/issues/comments/$TRIGGER_ID/reactions" \ | ||
| -f content=eyes >/dev/null 2>&1; then |
There was a problem hiding this comment.
[suggestion] 2>/dev/null discards the API error at exactly the moment it is worth having. This change exists so a human can tell "seen" from "dropped" without opening the Actions tab; when the reaction itself fails, the warning says only that it failed, and the log no longer distinguishes a 403 (the caller did not grant issues: write) from a 404 (wrong comment id) from a rate limit — so the diagnosis moves back into the workflow log the change set out to avoid, minus the one line that would have explained it.
Capturing stderr and folding it into the warning keeps that, e.g.:
if err=$(gh api -X POST "repos/$REPO/issues/comments/$TRIGGER_ID/reactions" \
-f content=eyes 2>&1 >/dev/null); then
echo "acknowledged comment $TRIGGER_ID"
else
echo "::warning::could not react to comment $TRIGGER_ID; the review continues: $(printf '%s' "$err" | tr '\n' ' ')"
fiThe tr matters: an annotation is single-line, so a multi-line gh error would otherwise truncate at the first newline.
| if: ${{ needs.guard.outputs.comment_id != '' }} | ||
| continue-on-error: true | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} |
There was a problem hiding this comment.
[nit] The reaction lands as github-actions[bot] rather than as seidroid, because the identity mint runs later in the job. In a repository that also runs ai-review.yml under that same default identity, the 👀 does not say which automation saw the trigger — a weaker signal than the sticky verdict that follows it.
Not a straightforward fix, and reasonable to leave as is: the App token is scoped to review_repo_name || github.event.repository.name, so on a cross-repository trigger it is scoped to the reviewed repo and could not react to a comment living here. github.token is the only credential that always covers the comment being reacted to. If it seems worth aligning the attribution for the same-repo path, that would mean moving the mint above this step and falling back to github.token; otherwise the current choice is the correct one and the tradeoff is worth a line in the step's comment.
What
Reacts 👀 to the triggering comment as the first step of the
reviewjob.Why
A review takes minutes to produce its first visible output — the Go
toolchain, the driver install, and the session start all run before anything
appears on the pull request. Until then nothing on the PR distinguishes "the
trigger was seen" from "the trigger was dropped", and the person who asked
has to open the Actions tab to find out which.
This is not hypothetical. During a credential outage, runs completed
successand posted nothing, and the only way to tell a working review froma broken one was to read the workflow log. An acknowledgement would have
separated the two immediately.
It is the first step deliberately. An acknowledgement that arrives after the
verdict is not one.
Scope
pull_requestreview has no comment toreact to, so the guard leaves
comment_idempty and the step is skipped.continue-on-error. An acknowledgement is a courtesy. Failing thereview because a reaction did not post would trade the whole job for the
signal that the job started. A failure emits a
::warning::instead.review on the same comment returns the existing reaction rather than adding
a second one. A retry needs no cleanup.
The permission
The job gains
issues: write. A reaction on a pull request comment goes tothe
/repos/{repo}/issues/comments/{id}/reactionsendpoint, whichpull-requests: writedoes not cover — the likely reason this was neverwired up.
Verification
actionlintis clean on the new step; the four findings it reports arepre-existing, at lines 326/1001/1177.
Behaviour needs a live trigger to confirm, since it depends on the token's
effective permissions in the calling repository. The consumer-side pin bump
is sei-load#97.
🤖 Generated with Claude Code