GitHub Issue Triage
Use this skill to run the daily open-source GitHub triage routine for CascadeGuard. It handles the full lifecycle: scan, label, prioritize, detect vulnerabilities, track PRs, report to the board, and hand off approved work into Paperclip.
Prerequisites
GITHUB_TOKENenvironment variable withrepo,security_events, andread:orgscopes on thecascadeguardorgPUSHOVER_APP_TOKENandPUSHOVER_USER_KEYenvironment variables for digest delivery- Paperclip agent context (
PAPERCLIP_API_KEY,PAPERCLIP_COMPANY_ID, etc.)
Multi-Repo Scope
All triage operations cover every public repo in the cascadeguard org. The
known repos are:
| Repository | Purpose |
|---|---|
cascadeguard/cascadeguard | Core product |
cascadeguard/cascadeguard-open-secure-images | Curated secure base images |
cascadeguard/cascadeguard-app | Web application |
cascadeguard/cascadeguard-docs | Documentation site |
cascadeguard/cascadeguard-exemplar | Example/reference configs |
Auto-Discover New Repos
On each triage run, discover any new public repos in the org so they are automatically included.
# List all public repos in the org
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/orgs/cascadeguard/repos?type=public&per_page=100" \
| jq -r '.[].full_name'Use the resulting list as $REPOS for all subsequent steps. Any repo not in
the known list above should be flagged in the digest as newly discovered.
Label Taxonomy
Create these labels on every repo in $REPOS if they do not already exist.
| Label | Color | Meaning |
|---|---|---|
triaged | #0e8a16 | Issue has been reviewed and categorized |
inscope | #1d76db | Fits the current roadmap (v1) |
next | #fbca04 | Top 10 prioritized items ready for CTO review |
cto-reviewed | #d93f0b | CTO has added assessment and solution design |
ready | #0075ca | Board-approved, can be pulled into Paperclip today |
needs-info | #e4e669 | Unclear scope, waiting for clarification from author |
security | #b60205 | Security-related issue or vulnerability report |
Ensure Labels Exist (All Repos)
for repo in $REPOS; do
for label in triaged inscope next cto-reviewed ready needs-info security; do
color=""
case $label in
triaged) color="0e8a16" ;;
inscope) color="1d76db" ;;
next) color="fbca04" ;;
cto-reviewed) color="d93f0b" ;;
ready) color="0075ca" ;;
needs-info) color="e4e669" ;;
security) color="b60205" ;;
esac
curl -s -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/labels" \
-d "{\"name\":\"$label\",\"color\":\"$color\"}" 2>/dev/null
done
doneTriage Workflow
Execute these steps in order during each daily triage heartbeat.
Step 1 — Vulnerability Detection (IMMEDIATE)
This step runs first and has immediate-action SLAs. Do NOT batch vuln handling into the daily digest.
For each repo in $REPOS:
1a. Scan for Vulnerability Reports in Public Issues
Check all new/untriaged issues for patterns that indicate a vulnerability report (keywords: “vulnerability”, “CVE”, “security flaw”, “exploit”, “injection”, “RCE”, “XSS”, “CSRF”, “auth bypass”, “privilege escalation”, “disclosure”, etc.).
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/issues?state=open&per_page=100" \
| jq '[.[] | select(.pull_request == null) | select(.labels | map(.name) | index("triaged") | not)]'
doneIf a public issue looks like a vulnerability report:
- Immediately close the issue with a comment:
Thank you for reporting this. For security issues, please use our private security advisory process instead. We have moved this report to a private advisory for investigation. See SECURITY.md for our responsible disclosure policy.
- Create a GitHub Security Advisory (draft) on the affected repo with the issue details.
- Notify the board immediately via Pushover with priority=1 (high):
curl -s -X POST "https://api.pushover.net/1/messages.json" \ -d "token=$PUSHOVER_APP_TOKEN" \ -d "user=$PUSHOVER_USER_KEY" \ -d "title=⚠️ Vuln Report Detected — $repo" \ -d "message=Public issue #$issue_number closed and moved to private advisory. Immediate review required." \ -d "priority=1" \ -d "retry=300" \ -d "expire=3600" \ -d "html=1"
1b. Track GitHub Security Advisories
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/security-advisories?state=draft,triage" \
2>/dev/null
doneTrack all open advisories and their state. Include in the daily digest.
1c. Vulnerability SLA Tracking
| SLA | Target |
|---|---|
| Acknowledgement | 24 hours |
| Assessment complete | 48 hours |
| Critical fix | 24 hours |
| High fix | 48 hours |
For each open advisory, calculate time elapsed since creation and flag any SLA breaches. Critical and High severity vulns that breach SLA trigger an immediate Pushover notification (priority=1), not batched into the daily digest.
Step 2 — Triage New Issues (All Repos)
For each repo in $REPOS, scan for issues that do not have the triaged label.
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/issues?state=open&per_page=100" \
| jq '[.[] | select(.pull_request == null) | select(.labels | map(.name) | index("triaged") | not)]'
doneFor each untriaged issue:
- Categorize: bug, feature request, question, security, docs
- Assess scope against the current v1 roadmap
- Label:
- In scope: add
triaged+inscope - Security: add
triaged+security(and follow Step 1 vuln handling) - Out of scope: convert to GitHub Discussion with an explanatory comment
- Unclear: add
triaged+needs-info, comment asking for clarification
- In scope: add
# Add labels to an issue (substitute repo and issue_number)
curl -s -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/issues/{issue_number}/labels" \
-d '{"labels":["triaged","inscope"]}'Step 3 — Prioritize In-Scope Issues
Review all issues with the inscope label across all repos. Rank by impact,
urgency, and alignment with current sprint goals.
- Apply
nextlabel to the top 10 highest priority items - Remove
nextfrom any issues that have fallen out of the top 10
Step 4 — CTO Review
For any next issue not yet labeled cto-reviewed:
- Add a detailed technical assessment comment with:
- Complexity estimate (S/M/L/XL)
- Solution design outline
- Dependencies and risks
- Suggested assignee capacity (% of sprint)
- Create or update PRDs/ADRs as appropriate
- Add the
cto-reviewedlabel
Step 5 — PR Review Tracking (All Repos)
Scan all open PRs across every repo in $REPOS.
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/pulls?state=open&per_page=100"
doneFor each open PR, collect:
- PR number, title, author
- Age (time since creation)
- Review status: approved, changes requested, pending, no reviewers assigned
- CI status: passing, failing, pending
# Get review status for a PR
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/pulls/{pr_number}/reviews"
# Get CI/check status for a PR
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/commits/{head_sha}/check-runs"Flags to raise:
- PRs with no reviewers assigned → auto-request review from CTO/engineer
- PRs awaiting review for >24 hours → flag in digest as stale
- PRs with failing CI → flag in digest
Ensure all PRs have an engineer or CTO assigned as reviewer:
# Request review if none assigned
curl -s -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/pulls/{pr_number}/requested_reviewers" \
-d '{"reviewers":["cto-github-username"]}'Step 6 — Generate Daily Digest
Build a summary containing:
- Vulnerability report:
- Open security advisories by repo (with SLA status)
- Any vuln reports detected and closed today
- SLA breaches (highlighted)
- PR review status (grouped by repo):
- All open PRs with age, author, review status, CI status
- Stale PRs (>24h without review)
- PRs missing reviewers
- New issues: issues triaged today with recommended labels/actions
- Triage status: counts by label (
inscope,next,needs-info, moved to discussions) - In-scope pipeline: summary of all
inscopeissues and current state - Today’s top 3: highest-priority
next+cto-revieweditems recommended to start - Board decisions needed: items requiring approval or scope clarification
- GitHub monitoring (see Step 7):
- Dependabot alert summary
- Actions workflow failures
- Discussions trending topics
- Stars/forks weekly trend
- Release/tag status
- Newly discovered repos (if any)
Deliver via Pushover
curl -s -X POST "https://api.pushover.net/1/messages.json" \
-d "token=$PUSHOVER_APP_TOKEN" \
-d "user=$PUSHOVER_USER_KEY" \
-d "title=CascadeGuard Daily Triage" \
-d "message=$DIGEST_TEXT" \
-d "priority=0" \
-d "html=1"Also post the full digest as a Paperclip comment on the triage task for history and auditability.
Step 7 — Additional GitHub Monitoring (All Repos)
Collect this data for inclusion in the daily digest.
Dependabot Alerts
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/dependabot/alerts?state=open&per_page=100"
doneGroup by severity (critical, high, medium, low) and repo.
GitHub Actions Workflow Failures
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/actions/runs?status=failure&per_page=10"
doneReport failures from the last 24 hours, grouped by workflow name and repo.
GitHub Discussions Trending Topics
# Use GraphQL for discussions
for repo in $REPOS; do
curl -s -X POST "https://api.github.com/graphql" \
-H "Authorization: bearer $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"query { repository(owner: \\\"cascadeguard\\\", name: \\\"${repo#*/}\\\") { discussions(first: 10, orderBy: {field: UPDATED_AT, direction: DESC}) { nodes { title url number comments { totalCount } updatedAt } } } }\"
}"
doneInclude top 5 most active discussions across all repos.
Stars/Forks Weekly Trend
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo" \
| jq '{repo: .full_name, stars: .stargazers_count, forks: .forks_count}'
doneCompare with previous week’s snapshot (stored in Paperclip comment history) and report delta.
Release/Tag Status
for repo in $REPOS; do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/releases?per_page=5"
doneReport latest release per repo and any draft releases pending publication.
Step 8 — Board Approval Gate
No work starts without board sign-off.
- Board reviews the digest (via Pushover notification linking to Paperclip comment)
- Board responds via Paperclip comment on the triage task
- Approved items get the
readylabel on GitHub - Only then are Paperclip issues created and assigned
# Add ready label after board approval (substitute repo and issue_number)
curl -s -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$repo/issues/{issue_number}/labels" \
-d '{"labels":["ready"]}'Create Paperclip Issue for Approved Work
curl -s -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
-d '{
"title": "Implement GitHub {repo}#{issue_number}: {title}",
"description": "From GitHub issue {repo}#{issue_number}.\n\nLink: https://github.com/{repo}/issues/{issue_number}",
"status": "todo",
"priority": "{priority}",
"parentId": "{parent_task_id}",
"goalId": "{goal_id}",
"projectId": "{project_id}",
"assigneeAgentId": "{assignee_agent_id}"
}'Cross-link: add a comment on the GitHub issue with the Paperclip task reference.
Step 9 — If No Board Response
If no board response within 24 hours, the next digest flags unanswered items again. Nothing proceeds without sign-off.
Moving Issues to Discussions
When an issue is out of scope, convert it to a GitHub Discussion rather than closing it, to preserve the conversation for future consideration.
# Use GitHub GraphQL API to convert issue to discussion
# Requires the discussion category ID (e.g., "Ideas" or "Feature Requests")
curl -s -X POST "https://api.github.com/graphql" \
-H "Authorization: bearer $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { convertIssuesToDiscussion(input: {issueId: \"{issue_node_id}\", categoryId: \"{category_node_id}\"}) { discussion { id url } } }"
}'PR Triage for Community Contributions
External PRs follow the same triage flow (labels on the PR), across all repos:
- CTO reviews code quality, test coverage, and architecture alignment
- Board approves merge (consistent with SDLC manual approval gate)
- Agent comments on the GitHub PR with status updates
Notes
- GitHub is the source of truth for issue state until board approval
- Paperclip issues are only created when work is approved and scheduled — do NOT create Paperclip tickets for raw CVE findings, scan results, or Dependabot alerts. These stay in GitHub as issues/advisories. A Paperclip ticket is only warranted when an agent needs to take a concrete action (code change, config update, investigation) and the board has approved it.
- 20% of Lead Platform Engineer capacity is allocated to community work
- WhatsApp via OpenClaw is a Phase 3 enhancement (Pushover is day-one)
- Vulnerability handling is immediate/non-batched; all other monitoring is daily
GITHUB_TOKENmust have org-level access for multi-repo operations