-
Notifications
You must be signed in to change notification settings - Fork 0
React with emoji for better UX on explicit review ask #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,8 @@ run-name: UCI / AI Review | |
| # never see the app token or any write scope. | ||
| # * claude_review -> consumes BOTH, does its own review, MERGES all three, returns TYPED | ||
| # output, then a github-script step posts one PR review + a check run. | ||
| # * complete_review_reaction -> clears the in-progress reaction and, when the review | ||
| # succeeds, marks an explicit request complete. | ||
| # | ||
| # Skip switch: label a PR with the `skip-review-label` input value (default `ai: skip-review`) | ||
| # and the whole pipeline is skipped -- no scouts, no Claude review, no PR review, no | ||
|
|
@@ -134,9 +136,10 @@ jobs: | |
| }} | ||
| permissions: | ||
| contents: read | ||
| pull-requests: read | ||
| pull-requests: write | ||
| outputs: | ||
| should_run: ${{ steps.resolve.outputs.should_run }} | ||
| reaction_subject_id: ${{ steps.resolve.outputs.reaction_subject_id }} | ||
| pr_number: ${{ steps.resolve.outputs.pr_number }} | ||
| head_sha: ${{ steps.resolve.outputs.head_sha }} | ||
| base_sha: ${{ steps.resolve.outputs.base_sha }} | ||
|
|
@@ -299,8 +302,34 @@ jobs: | |
| } else { | ||
| core.notice("allowed-team is empty or invalid; denying request."); | ||
| } | ||
|
|
||
| core.setOutput("should_run", String(authorized)); | ||
| if (!authorized) core.notice(`${actor} is not authorized to request a seidroid review.`); | ||
| if (!authorized) { | ||
| core.notice(`${actor} is not authorized to request a seidroid review.`); | ||
| return; | ||
| } | ||
|
|
||
| const subjectId = eventName === "pull_request_review" | ||
| ? context.payload.review?.node_id | ||
| : context.payload.comment?.node_id; | ||
| if (!subjectId) { | ||
| core.warning("The review request has no reactable GitHub node ID."); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| await github.graphql( | ||
| `mutation($subjectId: ID!) { | ||
| addReaction(input: {subjectId: $subjectId, content: EYES}) { | ||
| reaction { content } | ||
| } | ||
| }`, | ||
| { subjectId }, | ||
| ); | ||
| core.setOutput("reaction_subject_id", subjectId); | ||
| } catch (error) { | ||
| core.warning(`Could not add the in-progress reaction: ${error.message}`); | ||
| } | ||
|
|
||
| - name: Fetch seidroid prompt files | ||
| if: steps.resolve.outputs.should_run == 'true' | ||
|
|
@@ -1019,4 +1048,66 @@ jobs: | |
| title: `Claude + Codex + Cursor Review: ${verdict}`, | ||
| summary: summary || "No summary provided.", | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| complete_review_reaction: | ||
| name: Complete review reaction | ||
| needs: [preflight, codex_review, cursor_review, claude_review] | ||
| if: ${{ always() && needs.preflight.outputs.reaction_subject_id != '' }} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [blocker] The 👍 is posted unconditionally, so a failed review is reported as a successful one. The condition only checks
Gate the success reaction on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] Cheapest mitigation: have the preflight reaction step also remove any pre-existing EYES reaction from the same subject before adding a fresh one, so a subsequent run self-heals the stale state. |
||
| runs-on: ${{ inputs.runs-on }} | ||
| permissions: | ||
| pull-requests: write | ||
| steps: | ||
| - name: Detect app credentials | ||
| id: creds | ||
| env: | ||
| APP_ID: ${{ secrets.PLATFORM_CODE_AGENT_APP_ID }} | ||
| run: | | ||
| if [ -n "$APP_ID" ]; then echo "present=true" >> "$GITHUB_OUTPUT"; else echo "present=false" >> "$GITHUB_OUTPUT"; fi | ||
|
|
||
| - name: Generate GitHub App token | ||
| id: app-token | ||
| if: steps.creds.outputs.present == 'true' | ||
| continue-on-error: true | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] |
||
| uses: actions/create-github-app-token@v3 | ||
| with: | ||
| app-id: ${{ secrets.PLATFORM_CODE_AGENT_APP_ID }} | ||
| private-key: ${{ secrets.PLATFORM_CODE_AGENT_APP_PK }} | ||
| owner: ${{ github.repository_owner }} | ||
| repositories: ${{ github.event.repository.name }} | ||
|
|
||
| - name: Mark review request complete | ||
| uses: actions/github-script@v9 | ||
| env: | ||
| SUBJECT_ID: ${{ needs.preflight.outputs.reaction_subject_id }} | ||
| REVIEW_SUCCEEDED: ${{ needs.claude_review.result == 'success' }} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] When |
||
| with: | ||
| github-token: ${{ steps.app-token.outputs.token || github.token }} | ||
| script: | | ||
| const subjectId = process.env.SUBJECT_ID; | ||
| if (process.env.REVIEW_SUCCEEDED === "true") { | ||
| try { | ||
| await github.graphql( | ||
| `mutation($subjectId: ID!) { | ||
| addReaction(input: {subjectId: $subjectId, content: THUMBS_UP}) { | ||
| reaction { content } | ||
| } | ||
| }`, | ||
| { subjectId }, | ||
| ); | ||
| } catch (error) { | ||
| core.warning(`Could not add the completion reaction: ${error.message}`); | ||
| } | ||
| } | ||
| try { | ||
| await github.graphql( | ||
| `mutation($subjectId: ID!) { | ||
| removeReaction(input: {subjectId: $subjectId, content: EYES}) { | ||
| subject { id } | ||
| } | ||
| }`, | ||
| { subjectId }, | ||
| ); | ||
| } catch (error) { | ||
| core.warning(`Could not clear the in-progress reaction: ${error.message}`); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion] The most common trigger path is
issue_comment(a@seidroid reviewcomment on the PR conversation), whosenode_idis anIssueComment. Reactions on issue comments are gated by the Issues permission, not Pull requests — this repo's ownai-assistant.ymldeclaresissues: write # reply on the PR conversation timelinefor exactly that (.github/workflows/ai-assistant.yml:94) and reacts viarepos/.../issues/comments/{id}/reactions.So when app credentials are absent and
github.tokenis used as the fallback,addReactionhere (andremoveReactionincomplete_review_reaction, which grants the same scope at line 1059) will 403 and only emit a warning — reactions silently never appear. Note this is a two-part fix: because a reusable workflow cannot elevate beyond the caller's grant, addingissues: writeto these job blocks also requires adding it to.github/workflows/ai-review-self.ymland to the caller snippet in.github/seidroid/ai-review/README.md, otherwise the run fails outright for callers that don't grant it.