Add immediate centralized command reactions and propagate desired reaction context#31847
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Enhances centralized slash-command routing by supporting per-route ai_reaction metadata, adding an immediate reaction on command match, and propagating the intended reaction into dispatched workflow context.
Changes:
- Extend centralized routing payload entries to include
ai_reaction. - Add immediate reaction posting in
route_slash_command.cjsand propagatedesired_ai_reactionviaaw_context. - Update Go workflow generator and tests to emit/validate
ai_reactionrouting metadata.
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/central_slash_command_workflow.go | Adds ai_reaction to centralized route payload and resolves per-route reaction at compile time. |
| pkg/workflow/central_slash_command_workflow_test.go | Updates assertions for emitted routing JSON and adds reaction-targeting test. |
| go.mod | Promotes gopkg.in/yaml.v3 to a direct dependency. |
| actions/setup/js/route_slash_command.cjs | Implements immediate reaction behavior and injects desired_ai_reaction into dispatch context. |
| actions/setup/js/route_slash_command.test.cjs | Adds tests for immediate reactions and context propagation. |
| .github/workflows/agentic_commands.yml | Updates generated routing metadata to include ai_reaction for routes. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 6/6 changed files
- Comments generated: 4
| func resolveCentralizedRouteReaction(wd *WorkflowData, routeEvents []string) string { | ||
| if wd == nil || wd.AIReaction == "" || wd.AIReaction == "none" { | ||
| return "" | ||
| } | ||
|
|
||
| for _, eventName := range routeEvents { | ||
| switch eventName { | ||
| case "issues", "issue_comment": | ||
| if shouldIncludeIssueReactions(wd) { | ||
| return wd.AIReaction | ||
| } | ||
| case "pull_request", "pull_request_comment", "pull_request_review_comment": | ||
| if shouldIncludePullRequestReactions(wd) { | ||
| return wd.AIReaction | ||
| } | ||
| case "discussion", "discussion_comment": | ||
| if shouldIncludeDiscussionReactions(wd) { | ||
| return wd.AIReaction | ||
| } | ||
| } |
| const { owner, repo } = context.repo; | ||
| try { | ||
| switch (context.eventName) { | ||
| case "issues": { | ||
| const issueNumber = context.payload?.issue?.number; | ||
| if (!issueNumber) { | ||
| core.warning("Skipping immediate reaction: issue number was not found in payload."); | ||
| return; | ||
| } | ||
| await github.request(`POST /repos/${owner}/${repo}/issues/${issueNumber}/reactions`, { | ||
| content: normalized, | ||
| headers: { Accept: "application/vnd.github+json" }, | ||
| }); | ||
| return; | ||
| } | ||
| case "issue_comment": { | ||
| const commentId = context.payload?.comment?.id; | ||
| if (!commentId) { | ||
| core.warning("Skipping immediate reaction: comment id was not found in payload."); | ||
| return; | ||
| } | ||
| await github.request(`POST /repos/${owner}/${repo}/issues/comments/${commentId}/reactions`, { | ||
| content: normalized, | ||
| headers: { Accept: "application/vnd.github+json" }, | ||
| }); | ||
| return; | ||
| } | ||
| case "pull_request": { | ||
| const prNumber = context.payload?.pull_request?.number; | ||
| if (!prNumber) { | ||
| core.warning("Skipping immediate reaction: pull request number was not found in payload."); | ||
| return; | ||
| } | ||
| await github.request(`POST /repos/${owner}/${repo}/issues/${prNumber}/reactions`, { | ||
| content: normalized, | ||
| headers: { Accept: "application/vnd.github+json" }, | ||
| }); |
| const REACTION_MAP = { | ||
| "+1": "THUMBS_UP", | ||
| "-1": "THUMBS_DOWN", | ||
| laugh: "LAUGH", | ||
| confused: "CONFUSED", | ||
| heart: "HEART", | ||
| hooray: "HOORAY", | ||
| rocket: "ROCKET", | ||
| eyes: "EYES", | ||
| }; | ||
|
|
||
| function normalizeReaction(reaction) { | ||
| if (typeof reaction !== "string") { | ||
| return ""; | ||
| } | ||
| const trimmed = reaction.trim(); | ||
| if (!trimmed || trimmed === "none" || !Object.hasOwn(REACTION_MAP, trimmed)) { | ||
| return ""; | ||
| } | ||
| return trimmed; | ||
| } |
| case "discussion_comment": { | ||
| const commentNodeId = context.payload?.comment?.node_id; | ||
| if (!commentNodeId) { | ||
| core.warning("Skipping immediate reaction: discussion comment node id was not found in payload."); | ||
| return; | ||
| } |
|
@copilot review all comments |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in |
✨ Enhancement
This change improves centralized slash-command UX by reacting immediately when a matched command defines
ai_reaction, instead of waiting for the dispatched workflow to start. It also carries the chosen reaction in the dispatched workflow context so downstream jobs can read the intended reaction explicitly.What does this improve?
agentic_commands.ymlrouting flow.Why is this valuable?
Implementation approach:
ai_reaction.route_slash_command.cjs) to:desired_ai_reactionintoaw_contextwhen dispatching child workflows.ai_reactionin routing metadata.