Skip to content

Commit 741987e

Browse files
committed
FEAT: AI-powered issue triage with GitHub Models
- Add code-grounded analysis: fetch source files before AI calls for accuracy - Add engineer guidance for non-bug issues (FEATURE_REQUEST/DISCUSSION) - Replace speculative code changes with evidence-based sections - Add verdict labels (Confirmed Bug/Likely Bug/Require More Analysis/Not a Bug) - Format Teams notifications with bold labels and proper HTML rendering - Add local test script (test-triage-local.js) for development - Update .gitignore for triage test output files
1 parent d7e7e40 commit 741987e

4 files changed

Lines changed: 639 additions & 192 deletions

File tree

β€Ž.github/workflows/issue-notify.ymlβ€Ž

Lines changed: 90 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ on:
2828
required: false
2929
type: string
3030
default: ''
31+
engineer_guidance:
32+
required: false
33+
type: string
34+
default: ''
3135
issue_number:
3236
required: true
3337
type: string
@@ -61,6 +65,7 @@ jobs:
6165
INPUT_RELEVANT_FILES: ${{ inputs.relevant_files }}
6266
INPUT_SUMMARY: ${{ inputs.summary_for_maintainers }}
6367
INPUT_CODE_ANALYSIS: ${{ inputs.code_analysis }}
68+
INPUT_ENGINEER_GUIDANCE: ${{ inputs.engineer_guidance }}
6469
INPUT_ACTION_TEXT: ${{ inputs.justification }}
6570
TEAMS_WEBHOOK_URL: ${{ secrets.TEAMS_WEBHOOK_URL }}
6671
run: |
@@ -96,21 +101,68 @@ jobs:
96101
;;
97102
esac
98103
99-
# Truncate code analysis if too long
100-
CODE_ANALYSIS="$INPUT_CODE_ANALYSIS"
101-
if [ ${#CODE_ANALYSIS} -gt 1500 ]; then
102-
CODE_ANALYSIS="${CODE_ANALYSIS:0:1500}... (see Actions log for full analysis)"
103-
fi
104-
if [ -z "$CODE_ANALYSIS" ]; then
104+
# Parse and format code analysis from JSON into readable text
105+
CODE_ANALYSIS_RAW="$INPUT_CODE_ANALYSIS"
106+
if [ -n "$CODE_ANALYSIS_RAW" ]; then
107+
# Try to parse as JSON and extract structured fields
108+
CODE_ANALYSIS=$(echo "$CODE_ANALYSIS_RAW" | jq -r '
109+
[
110+
(if .is_bug then "Verdict: " + .is_bug else empty end),
111+
(if .root_cause then "\nRoot Cause: " + .root_cause else empty end),
112+
(if .affected_components and (.affected_components | length) > 0
113+
then "\nAffected Components:\n" + ([.affected_components[] | " β€’ " + .] | join("\n"))
114+
else empty end),
115+
(if .evidence_and_context then "\nEvidence & Context: " + .evidence_and_context else empty end),
116+
(if .recommended_fixes and (.recommended_fixes | length) > 0
117+
then "\nRecommended Fixes:\n" + ([.recommended_fixes | to_entries[] | " " + ((.key + 1) | tostring) + ". " + .value] | join("\n"))
118+
else empty end),
119+
(if .code_locations and (.code_locations | length) > 0
120+
then "\nCode Locations:\n" + ([.code_locations[] | " β€’ " + .] | join("\n"))
121+
else empty end),
122+
(if .risk_assessment then "\nRisk Assessment: " + .risk_assessment else empty end)
123+
] | join("\n")
124+
' 2>/dev/null || echo "$CODE_ANALYSIS_RAW")
125+
else
105126
CODE_ANALYSIS="N/A β€” classification did not require code analysis."
106127
fi
107128
108-
# Build Adaptive Card payload using jq for proper JSON escaping
129+
# Parse and format engineer guidance from JSON into readable text
130+
ENGINEER_GUIDANCE_RAW="$INPUT_ENGINEER_GUIDANCE"
131+
if [ -n "$ENGINEER_GUIDANCE_RAW" ]; then
132+
ENGINEER_GUIDANCE=$(echo "$ENGINEER_GUIDANCE_RAW" | jq -r '
133+
[
134+
(if .technical_assessment then "<b>Technical Assessment:</b> " + .technical_assessment else empty end),
135+
(if .verdict then "<b>Verdict:</b> " + .verdict else empty end),
136+
(if .effort_estimate then "Effort Estimate: " + .effort_estimate else empty end),
137+
(if .affected_files and (.affected_files | length) > 0
138+
then "<b>Affected Files:</b><br>" + ([.affected_files[] | "&nbsp;&nbsp;β€’ " + .] | join("<br>"))
139+
else empty end),
140+
(if .implementation_approach then "<b>Implementation Approach:</b> " + .implementation_approach else empty end),
141+
(if .risks_and_tradeoffs then "<b>Risks &amp; Tradeoffs:</b> " + .risks_and_tradeoffs else empty end),
142+
(if .suggested_response then "<b>Suggested Response to User:</b><br>" + .suggested_response else empty end),
143+
(if .related_considerations and (.related_considerations | length) > 0
144+
then "<b>Related Considerations:</b><br>" + ([.related_considerations | to_entries[] | "&nbsp;&nbsp;" + ((.key + 1) | tostring) + ". " + .value] | join("<br>"))
145+
else empty end)
146+
] | join("<br><br>")
147+
' 2>/dev/null || echo "$ENGINEER_GUIDANCE_RAW")
148+
else
149+
ENGINEER_GUIDANCE=""
150+
fi
151+
152+
# Set severity color indicator
153+
case "$SEVERITY" in
154+
critical) SEV_INDICATOR="πŸ”΄" ;;
155+
high) SEV_INDICATOR="🟠" ;;
156+
medium) SEV_INDICATOR="🟑" ;;
157+
*) SEV_INDICATOR="🟒" ;;
158+
esac
159+
160+
# Build well-formatted HTML message using jq for proper JSON escaping
109161
jq -n \
110162
--arg emoji "$EMOJI" \
111-
--arg category "$CATEGORY" \
112163
--arg category_display "$CATEGORY_DISPLAY" \
113164
--arg severity "$SEVERITY" \
165+
--arg sev_indicator "$SEV_INDICATOR" \
114166
--arg confidence "$INPUT_CONFIDENCE" \
115167
--arg issue_num "$INPUT_ISSUE_NUMBER" \
116168
--arg issue_title "$INPUT_ISSUE_TITLE" \
@@ -120,131 +172,38 @@ jobs:
120172
--arg relevant_files "$INPUT_RELEVANT_FILES" \
121173
--arg summary "$INPUT_SUMMARY" \
122174
--arg code_analysis "$CODE_ANALYSIS" \
175+
--arg engineer_guidance "$ENGINEER_GUIDANCE" \
123176
--arg action "$ACTION" \
177+
--arg repo_url "https://github.com/microsoft/mssql-python" \
124178
'{
125-
"type": "message",
126-
"attachments": [
127-
{
128-
"contentType": "application/vnd.microsoft.card.adaptive",
129-
"content": {
130-
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
131-
"type": "AdaptiveCard",
132-
"version": "1.4",
133-
"body": [
134-
{
135-
"type": "TextBlock",
136-
"size": "large",
137-
"weight": "bolder",
138-
"text": ($emoji + " mssql-python Issue Triage"),
139-
"wrap": true,
140-
"style": "heading"
141-
},
142-
{
143-
"type": "ColumnSet",
144-
"columns": [
145-
{
146-
"type": "Column",
147-
"width": "auto",
148-
"items": [
149-
{
150-
"type": "TextBlock",
151-
"text": $category_display,
152-
"weight": "bolder",
153-
"color": (if $category == "BREAK_FIX" then "attention"
154-
elif $category == "BUG" then "warning"
155-
else "default" end),
156-
"size": "medium"
157-
}
158-
]
159-
},
160-
{
161-
"type": "Column",
162-
"width": "auto",
163-
"items": [
164-
{
165-
"type": "TextBlock",
166-
"text": ("Severity: " + $severity),
167-
"size": "medium",
168-
"color": (if $severity == "critical" then "attention"
169-
elif $severity == "high" then "warning"
170-
else "default" end)
171-
}
172-
]
173-
},
174-
{
175-
"type": "Column",
176-
"width": "auto",
177-
"items": [
178-
{
179-
"type": "TextBlock",
180-
"text": ("Confidence: " + $confidence + "%"),
181-
"size": "medium"
182-
}
183-
]
184-
}
185-
]
186-
},
187-
{
188-
"type": "FactSet",
189-
"separator": true,
190-
"facts": [
191-
{ "title": "Issue", "value": ("#" + $issue_num + " β€” " + $issue_title) },
192-
{ "title": "Author", "value": ("@" + $issue_author) },
193-
{ "title": "Keywords", "value": $keywords },
194-
{ "title": "Relevant Files", "value": $relevant_files }
195-
]
196-
},
197-
{
198-
"type": "TextBlock",
199-
"text": "**πŸ“ Analysis**",
200-
"weight": "bolder",
201-
"spacing": "medium",
202-
"wrap": true
203-
},
204-
{
205-
"type": "TextBlock",
206-
"text": $summary,
207-
"wrap": true
208-
},
209-
{
210-
"type": "TextBlock",
211-
"text": "**πŸ” Code Analysis**",
212-
"weight": "bolder",
213-
"spacing": "medium",
214-
"wrap": true
215-
},
216-
{
217-
"type": "TextBlock",
218-
"text": $code_analysis,
219-
"wrap": true,
220-
"fontType": "monospace",
221-
"size": "small"
222-
},
223-
{
224-
"type": "TextBlock",
225-
"text": ("⚑ **Action Required:** " + $action),
226-
"weight": "bolder",
227-
"color": "attention",
228-
"spacing": "medium",
229-
"wrap": true,
230-
"separator": true
231-
}
232-
],
233-
"actions": [
234-
{
235-
"type": "Action.OpenUrl",
236-
"title": "πŸ“‹ View Issue",
237-
"url": $issue_url
238-
},
239-
{
240-
"type": "Action.OpenUrl",
241-
"title": "πŸ“‚ View Repository",
242-
"url": "https://github.com/microsoft/mssql-python"
243-
}
244-
]
245-
}
246-
}
247-
]
179+
"text": (
180+
"<h2>" + $emoji + " mssql-python Issue Triage</h2>" +
181+
"<p><b>" + $category_display + "</b> &nbsp;|&nbsp; " +
182+
$sev_indicator + " Severity: <b>" + $severity + "</b> &nbsp;|&nbsp; " +
183+
"Confidence: <b>" + $confidence + "%</b></p>" +
184+
"<hr>" +
185+
"<p>" +
186+
"πŸ“Œ <b>Issue:</b> <a href=\"" + $issue_url + "\">#" + $issue_num + " β€” " + $issue_title + "</a><br>" +
187+
"πŸ‘€ <b>Author:</b> @" + $issue_author + "<br>" +
188+
"🏷️ <b>Keywords:</b> " + $keywords + "<br>" +
189+
"πŸ“‚ <b>Relevant Files:</b> " + $relevant_files +
190+
"</p>" +
191+
"<hr>" +
192+
"<h3>πŸ“ Analysis</h3>" +
193+
"<p>" + $summary + "</p>" +
194+
"<h3>πŸ” Code Analysis</h3>" +
195+
"<p>" + $code_analysis + "</p>" +
196+
(if $engineer_guidance != "" then
197+
"<h3>πŸ’‘ Engineer Guidance</h3>" +
198+
"<p>" + $engineer_guidance + "</p>"
199+
else "" end) +
200+
"<hr>" +
201+
"<p>⚑ <b>Action Required:</b> " + $action + "</p>" +
202+
"<p><i>⚠️ AI-generated analysis β€” verified against source code but may contain inaccuracies. Review before acting.</i></p>" +
203+
"<p><a href=\"" + $issue_url + "\">πŸ“‹ View Issue</a>" +
204+
" &nbsp;|&nbsp; " +
205+
"<a href=\"" + $repo_url + "\">πŸ“‚ View Repository</a></p>"
206+
)
248207
}' > /tmp/teams_payload.json
249208
250209
echo "Sending notification to Teams Channel..."

0 commit comments

Comments
Β (0)