Problem
The current transfer loop prevention in gatekeeper has a false positive issue. It skips ALL recently created issues with action="opened", including legitimate new issues that were created directly in the repo.
Current Behavior
// This skips BOTH transferred AND new issues
if ctx.Issue.EventAction == "opened" && age < 2*time.Minute {
return ErrSkipPipeline // TOO AGGRESSIVE
}
Evidence
Test issue #38 in event-integrator-cli:
- Created directly in CLI repo (NOT transferred)
- Bot skipped it: "recently created issue (likely transferred)"
- ❌ False positive - legitimate new issue was not triaged
Root Cause
When GitHub transfers an issue:
- Source repo gets:
action="transferred" ✅
- Destination repo gets:
action="opened" ❌
Our heuristic (skip if opened + age < 2min) can't distinguish:
- Transferred issue (should skip)
- Real new issue (should NOT skip)
Both appear identical in the webhook event.
Proposed Solution
Implement proper transfer detection using GitHub API:
func (s *Gatekeeper) checkIfRecentlyTransferred(ctx *pipeline.Context) (bool, error) {
// Query GitHub API for issue events
events, err := githubClient.ListIssueEvents(
ctx.Issue.Org,
ctx.Issue.Repo,
ctx.Issue.Number,
)
// Look for recent transfer event (< 2 minutes)
for _, event := range events {
if event.Type == "transferred" && time.Since(event.CreatedAt) < 2*time.Minute {
return true, nil
}
}
return false, nil
}
Implementation Requirements
1. Add GitHub Client to Context
Currently, pipeline.Context doesn't have access to GitHub client. Need to:
- Add
GitHubClient field to Dependencies
- Pass dependencies through Context
- Or store client in gatekeeper constructor
2. Implement ListIssueEvents
The GitHub client needs a method to fetch issue timeline events:
func (c *Client) ListIssueEvents(ctx context.Context, org, repo string, number int) ([]Event, error)
3. Update Gatekeeper Logic
// Check EventAction first (fast path for source repos)
if ctx.Issue.EventAction == "transferred" {
return ErrSkipPipeline
}
// For "opened" events, check API to see if it was actually transferred
if ctx.Issue.EventAction == "opened" {
if wasTransferred, err := s.checkIfRecentlyTransferred(ctx); err == nil && wasTransferred {
return ErrSkipPipeline
}
}
Trade-offs
Pros ✅
- Accurate: No false positives
- Reliable: Checks actual transfer events
- Correct behavior: Real new issues get triaged
Cons ⚠️
- API call: Adds 100-200ms latency per issue
- Rate limits: Uses GitHub API quota
- Complexity: More code to maintain
Mitigation
- Cache transfer status for 5 minutes to avoid repeated API calls
- Only check for "opened" events (not all events)
- Fast-fail if API is unavailable (proceed with triage)
Acceptance Criteria
Test Cases
Test 1: Real New Issue
- Create issue directly in repo
- Bot should TRIAGE (not skip)
- ✅ No transfer event in timeline
Test 2: Transferred Issue
- Create issue in repo A
- Transfer to repo B
- Bot should SKIP in repo B
- ✅ Transfer event found in timeline
Test 3: Old Issue Edited
- Edit issue created 1 day ago
- Bot should TRIAGE normally
- ✅ Transfer check skipped (not "opened" event)
Current Workaround
Revert the time-based heuristic and accept transfer loops until proper fix is implemented.
References
Problem
The current transfer loop prevention in gatekeeper has a false positive issue. It skips ALL recently created issues with
action="opened", including legitimate new issues that were created directly in the repo.Current Behavior
Evidence
Test issue #38 in event-integrator-cli:
Root Cause
When GitHub transfers an issue:
action="transferred"✅action="opened"❌Our heuristic (skip if opened + age < 2min) can't distinguish:
Both appear identical in the webhook event.
Proposed Solution
Implement proper transfer detection using GitHub API:
Implementation Requirements
1. Add GitHub Client to Context
Currently,
pipeline.Contextdoesn't have access to GitHub client. Need to:GitHubClientfield to Dependencies2. Implement ListIssueEvents
The GitHub client needs a method to fetch issue timeline events:
3. Update Gatekeeper Logic
Trade-offs
Pros ✅
Cons⚠️
Mitigation
Acceptance Criteria
ListIssueEventsmethod implementedTest Cases
Test 1: Real New Issue
Test 2: Transferred Issue
Test 3: Old Issue Edited
Current Workaround
Revert the time-based heuristic and accept transfer loops until proper fix is implemented.
References