Skip to content

Commit ba3cf86

Browse files
feat(autoTriage): Enable auto-apply, security handling, and team config (#222)
* feat(autoTriage): Enable auto-apply, security handling, and team config - Add --apply flag to auto-triage workflow to actually apply labels/assignees - Add security issue detection with keyword matching and LLM analysis - Security issues auto-assign to Johan (pontemonti) with P1 priority - Update team-members.json with expertise for all engineers - Add escalation_chain (Sella + Johan as leads, Tahir as manager) - Add SLA hours config (P0:6h, P1:12h, P2:24h, P3/P4:72h) - Add Mrunal as Backend Engineer - Promote Johan to Tech Lead - Add PRD (design.md), tasks.md, and roadmap.md documentation * fix(autoTriage): Fix config loading and since_time bug - Fix get_default_config() to use _default_config() instead of missing sample-config.yml - Initialize since_time at start to fix UnboundLocalError when using issue_numbers mode * feat(autoTriage): Add re-triage on issue edits, labels, and comments (Phase 4) - Add triggers: issues.edited, issues.labeled, issue_comment.created - Add bot skip condition to prevent infinite loops - Add --retriage flag with 5-minute cooldown - Add was_recently_triaged() to check recent triage - Add update_or_add_triage_comment() to prevent duplicate comments * feat(autoTriage): Add daily issue report workflow with GitHub Summary * feat(autoTriage): Add SLA escalation and daily report (Phase 3) - Add escalation_service.py with SLA breach detection - Add escalation_check.py CLI for hourly SLA checks - Add escalate-stale-issues.yml workflow (hourly cron) - Add daily_report.py CLI for daily issue reports - Add daily-issue-report.yml workflow (daily at 9 AM UTC) - Add daily_report_service.py for report generation - Update team-members.json with new SLA hours (P0:24h, P1:48h, P2:72h, P3/P4:120h) - Add sla_hours and escalation_chain to TeamConfig model * fix(autoTriage): Change SLA escalation to daily instead of hourly * docs(autoTriage): Add comments for Teams/email setup * fix(autoTriage): Remove unused timedelta import * fix(autoTriage): Address code review comments and sync documentation - Remove emojis from Python output (use text indicators like [OK], [WARNING], [CRITICAL]) - Fix duplicate ERROR text in escalation_check.py - Simplify redundant if/else in auto-triage-issues.yml - Update SLA fallback defaults to match config (P0:24h, P1:48h, P2:72h, P3/P4:120h) - Sync design.md and tasks.md with actual implementation status - Fix escalation_chain format in design.md (lead is now an array) - Centralize get_sla_hours() to avoid duplication - Add TODO for placeholder Teams models - Update tasks.md: Phases 1,3,4,5,6 Complete; Phases 2,7 Not Started * fix(autoTriage): Address remaining Copilot code review comments - Use updated_at instead of created_at for SLA calculation in daily report - Use updated_at in was_recently_triaged to handle edited comments - Remove unused Any import from teams_service.py - Add sla_hours and escalation_chain to ConfigParser.parse() - Fix status_emoji -> status_indicator in Teams card - Fix wording 'Reassigning' -> 'Adding as assignee' for accuracy * fix(autoTriage): Address Copilot code review comments (round 2) - Remove eval from workflow, use bash array for safer command building - Remove Teams webhook from daily report (blocked by DLP) - Use SecurityConfig dataclass properly instead of dict - Update FR status in design.md (FR2,4,5,6,7 COMPLETE) - Fix SLA table in roadmap.md to match implementation (24/48/72/120) - Use exact matching for bot user detection (not substring) - Security priority only elevates, never downgrades - Update docs: 'add Tech Lead as assignee' not 'reassign' * fix(autoTriage): Address Copilot code review comments (round 3) - Remove webhook URL logging from TeamsService (security) - Defer TeamsService instantiation until actually needed (lazy init) - Return aggregated success from apply_escalation (assign + label + comment) - Defer teams_service creation in intake_service until post_to_teams is True * remove noise * test(autoTriage): Add comprehensive unit tests organized by category - Reorganize tests into subfolders: services/, models/, scripts/, workflows/, integration/ - Add test_models.py for PriorityRules, SecurityConfig, TeamConfig, IssueClassification, AdoModels - Add test_scripts.py for update_contributions.py contribution score calculation - Add test_workflows.py for GitHub Actions workflow logic validation - Add test_triage_pipeline.py for end-to-end integration tests - Add service-specific tests: test_daily_report_service.py, test_escalation_service.py, test_intake_service.py, test_teams_service.py, test_prompt_loader.py - Fix word boundary matching for short security keywords in llm_service.py - Fix redundant elif in intake_service.py priority elevation Total: 311 tests covering all services, models, scripts, and workflows * fix: address Copilot review comments - remove emojis and fix code issues - Replace emojis with text markers in workflows ([AUTO-TRIAGE], [OK], [INFO], etc.) - Fix misleading comment in auto-triage-issues.yml - Remove unreachable code in test_workflows.py - Improve shell injection pattern detection in tests - Only prefix @ for actual assignees, not 'Unassigned'
1 parent c62c356 commit ba3cf86

38 files changed

Lines changed: 4504 additions & 56 deletions

.github/workflows/auto-triage-issues.yml

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
name: Auto-Triage New Issues
1+
name: Auto-Triage Issues
22

33
on:
44
issues:
5-
types: [opened]
5+
types: [opened, edited]
66

77
permissions:
88
issues: write
@@ -12,6 +12,9 @@ jobs:
1212
triage:
1313
runs-on: ubuntu-latest
1414
name: Auto-triage issue with AI
15+
16+
# Skip if triggered by bot to avoid infinite loops
17+
if: github.actor != 'github-actions[bot]' && github.actor != 'dependabot[bot]'
1518

1619
steps:
1720
- name: Checkout repository
@@ -27,6 +30,19 @@ jobs:
2730
cd autoTriage
2831
pip install -r requirements.txt
2932
33+
- name: Determine issue number
34+
id: issue
35+
run: |
36+
# Get issue number from the issues event payload
37+
echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
38+
39+
# Check if this is a re-triage (not a new issue)
40+
if [ "${{ github.event.action }}" == "opened" ]; then
41+
echo "retriage=false" >> $GITHUB_OUTPUT
42+
else
43+
echo "retriage=true" >> $GITHUB_OUTPUT
44+
fi
45+
3046
- name: Run triage analysis
3147
id: triage
3248
env:
@@ -42,11 +58,21 @@ jobs:
4258
AZURE_OPENAI_API_VERSION: ${{ secrets.AZURE_OPENAI_API_VERSION }}
4359
run: |
4460
cd autoTriage
45-
python triage_issue.py \
46-
--owner "${{ github.repository_owner }}" \
47-
--repo "${{ github.event.repository.name }}" \
48-
--issue-number "${{ github.event.issue.number }}" \
61+
62+
# Build args array
63+
ARGS=(
64+
--owner "${{ github.repository_owner }}"
65+
--repo "${{ github.event.repository.name }}"
66+
--issue-number "${{ steps.issue.outputs.number }}"
4967
--output "${{ runner.temp }}/triage_result.json"
68+
--apply
69+
)
70+
71+
if [ "${{ steps.issue.outputs.retriage }}" == "true" ]; then
72+
ARGS+=(--retriage)
73+
fi
74+
75+
python triage_issue.py "${ARGS[@]}"
5076
5177
- name: Post triage results
5278
if: success()
@@ -80,7 +106,7 @@ jobs:
80106
}
81107
82108
// Build comment body
83-
let comment = '## 🤖 Auto-Triage Results\n\n';
109+
let comment = '## [AUTO-TRIAGE] Results\n\n';
84110
comment += `**Classification:** ${triageResult.issue_type || 'Unknown'}\n`;
85111
comment += `**Priority:** ${triageResult.priority || 'P3'}\n`;
86112
comment += `**Confidence:** ${triageResult.confidence ? (triageResult.confidence * 100).toFixed(1) + '%' : 'N/A'}\n\n`;
@@ -93,12 +119,12 @@ jobs:
93119
comment += rationaleText + '\n\n';
94120
95121
if (triageResult.is_copilot_fixable) {
96-
comment += ' This issue appears to be Copilot-fixable!\n\n';
122+
comment += '[OK] This issue appears to be Copilot-fixable!\n\n';
97123
}
98124
99125
// Add fix suggestions if available
100126
if (triageResult.fix_suggestions && triageResult.fix_suggestions.length > 0) {
101-
comment += '### 💡 Suggested Approach\n\n';
127+
comment += '### [SUGGESTION] Suggested Approach\n\n';
102128
triageResult.fix_suggestions.forEach((suggestion, index) => {
103129
comment += `${index + 1}. ${suggestion}\n`;
104130
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Daily Issue Report
2+
3+
on:
4+
schedule:
5+
# Run daily at 9 AM UTC (adjust as needed)
6+
- cron: '0 9 * * *'
7+
workflow_dispatch:
8+
9+
permissions:
10+
issues: read
11+
contents: read
12+
13+
jobs:
14+
daily-report:
15+
runs-on: ubuntu-latest
16+
name: Generate Daily Issue Report
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: '3.11'
26+
27+
- name: Install dependencies
28+
run: |
29+
cd autoTriage
30+
pip install -r requirements.txt
31+
32+
- name: Generate Daily Report
33+
env:
34+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
run: |
36+
cd autoTriage
37+
python daily_report.py --owner ${{ github.repository_owner }} --repo ${{ github.event.repository.name }} --output report.json --summary $GITHUB_STEP_SUMMARY
38+
39+
- name: Upload report artifact
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: daily-report
43+
path: autoTriage/report.json
44+
retention-days: 30
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
name: SLA Escalation Check
2+
3+
on:
4+
schedule:
5+
# Run daily at 8 AM UTC (before daily report at 9 AM)
6+
- cron: '0 8 * * *'
7+
workflow_dispatch:
8+
inputs:
9+
apply:
10+
description: 'Apply escalation actions (assign, comment)'
11+
required: false
12+
default: 'true'
13+
type: choice
14+
options:
15+
- 'true'
16+
- 'false'
17+
18+
permissions:
19+
issues: write
20+
contents: read
21+
22+
jobs:
23+
check-sla:
24+
runs-on: ubuntu-latest
25+
name: Check SLA breaches and escalate
26+
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
31+
- name: Set up Python
32+
uses: actions/setup-python@v5
33+
with:
34+
python-version: '3.11'
35+
36+
- name: Install dependencies
37+
run: |
38+
cd autoTriage
39+
pip install -r requirements.txt
40+
41+
- name: Run SLA escalation check
42+
id: escalation
43+
env:
44+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
run: |
46+
cd autoTriage
47+
48+
# Determine apply mode
49+
APPLY_FLAG=""
50+
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
51+
if [ "${{ inputs.apply }}" == "true" ]; then
52+
APPLY_FLAG="--apply"
53+
fi
54+
else
55+
# Scheduled runs always apply
56+
APPLY_FLAG="--apply"
57+
fi
58+
59+
python escalation_check.py \
60+
--owner "${{ github.repository_owner }}" \
61+
--repo "${{ github.event.repository.name }}" \
62+
--output "${{ runner.temp }}/escalation_result.json" \
63+
$APPLY_FLAG
64+
65+
- name: Upload escalation report
66+
if: always()
67+
uses: actions/upload-artifact@v4
68+
with:
69+
name: escalation-report
70+
path: ${{ runner.temp }}/escalation_result.json
71+
retention-days: 30
72+
73+
- name: Summary
74+
if: always()
75+
run: |
76+
if [ -f "${{ runner.temp }}/escalation_result.json" ]; then
77+
echo "## SLA Escalation Report" >> $GITHUB_STEP_SUMMARY
78+
echo "" >> $GITHUB_STEP_SUMMARY
79+
80+
TOTAL=$(cat "${{ runner.temp }}/escalation_result.json" | jq -r '.total_breached')
81+
echo "**Total SLA Breaches:** $TOTAL" >> $GITHUB_STEP_SUMMARY
82+
echo "" >> $GITHUB_STEP_SUMMARY
83+
84+
if [ "$TOTAL" -gt 0 ]; then
85+
echo "### Breached Issues" >> $GITHUB_STEP_SUMMARY
86+
echo "" >> $GITHUB_STEP_SUMMARY
87+
echo "| Issue | Priority | Hours Open | SLA | Action |" >> $GITHUB_STEP_SUMMARY
88+
echo "|-------|----------|------------|-----|--------|" >> $GITHUB_STEP_SUMMARY
89+
90+
cat "${{ runner.temp }}/escalation_result.json" | jq -r '.issues[] | "| #\(.number) | \(.priority) | \(.hours_open)h | \(.sla_hours)h | \(.escalation_action) |"' >> $GITHUB_STEP_SUMMARY
91+
else
92+
echo "All issues are within SLA thresholds." >> $GITHUB_STEP_SUMMARY
93+
fi
94+
fi

.github/workflows/update-team-workload.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ jobs:
7070
echo "" >> $GITHUB_STEP_SUMMARY
7171
7272
if [ "${{ steps.check_changes.outputs.has_changes }}" == "true" ]; then
73-
echo " Team workload updated based on current open issues" >> $GITHUB_STEP_SUMMARY
73+
echo "[OK] Team workload updated based on current open issues" >> $GITHUB_STEP_SUMMARY
7474
echo "" >> $GITHUB_STEP_SUMMARY
7575
echo "### Changes" >> $GITHUB_STEP_SUMMARY
7676
echo "\`\`\`diff" >> $GITHUB_STEP_SUMMARY
7777
git diff autoTriage/config/team-members.json >> $GITHUB_STEP_SUMMARY || echo "Changes committed" >> $GITHUB_STEP_SUMMARY
7878
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
7979
else
80-
echo "ℹ️ No workload changes - team is balanced" >> $GITHUB_STEP_SUMMARY
80+
echo "[INFO] No workload changes - team is balanced" >> $GITHUB_STEP_SUMMARY
8181
fi

autoTriage/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
report.json
2+
summary.md

autoTriage/config/team-members.json

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,35 @@
55
"role": "Tech Lead",
66
"login": "sellakumaran",
77
"contributions": 20,
8-
"expertise": ["architecture", "code review", "backend", "API design", "mentoring", "system design"]
8+
"expertise": ["architecture", "code review", "backend", "API design", "mentoring", "system design", "security"]
9+
},
10+
{
11+
"name": "Johan Broberg",
12+
"role": "Tech Lead",
13+
"login": "pontemonti",
14+
"contributions": 12,
15+
"expertise": ["backend", "infrastructure", "DevOps", "deployment", "Azure", "architecture", "security"]
916
},
1017
{
1118
"name": "Josh Oratz",
1219
"role": "Backend Engineer",
1320
"login": "joratz",
1421
"contributions": 18,
15-
"expertise": []
22+
"expertise": ["backend", "CLI", "dotnet", "csharp", "testing", "Azure"]
1623
},
1724
{
1825
"name": "Mengyi Xu",
1926
"role": "Backend Engineer",
2027
"login": "mengyimicro",
2128
"contributions": 15,
22-
"expertise": []
29+
"expertise": ["backend", "python", "automation", "GitHub Actions", "AI", "LLM integration"]
2330
},
2431
{
25-
"name": "Johan Broberg",
32+
"name": "Mrunal Hirve",
2633
"role": "Backend Engineer",
27-
"login": "pontemonti",
28-
"contributions": 12,
29-
"expertise": []
34+
"login": "mrunalhirve",
35+
"contributions": 10,
36+
"expertise": ["backend", "Azure", "AI", "automation", "project management"]
3037
},
3138
{
3239
"name": "Tahir Sousa",
@@ -35,5 +42,28 @@
3542
"contributions": 5,
3643
"expertise": ["management", "planning", "coordination", "strategic planning"]
3744
}
38-
]
45+
],
46+
"escalation_chain": {
47+
"lead": ["sellakumaran", "pontemonti"],
48+
"manager": "tmlsousa"
49+
},
50+
"sla_hours": {
51+
"P0": 24,
52+
"P1": 48,
53+
"P2": 72,
54+
"P3": 120,
55+
"P4": 120
56+
},
57+
"security": {
58+
"keywords": [
59+
"vulnerability", "CVE", "security", "exploit", "injection",
60+
"XSS", "CSRF", "SQL injection", "auth bypass", "authentication bypass",
61+
"privilege escalation", "remote code execution", "RCE", "buffer overflow",
62+
"denial of service", "DoS", "DDoS", "data leak", "data breach",
63+
"secrets exposed", "credential leak", "token leak", "API key exposed",
64+
"insecure", "unsafe", "malicious", "attack", "breach"
65+
],
66+
"assignee": "pontemonti",
67+
"default_priority": "P1"
68+
}
3969
}

0 commit comments

Comments
 (0)