Conversation
Summary of ChangesHello @indurireddy-TF, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request delivers a robust automation solution for managing GitHub issues by introducing a Go-based stale bot. The bot intelligently processes issues, identifying those requiring attention or closure based on predefined activity thresholds and maintainer input. It streamlines issue maintenance, ensuring that discussions remain active and unresolved items are handled efficiently, while also providing alerts for potentially overlooked changes. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new stale bot agent for GitHub repositories, implemented in Go using the ADK framework. The agent's core functionality is defined in agent.go, which includes fetching issue data via GraphQL, building a chronological event timeline, and replaying this history to determine an issue's current state (e.g., last actor, activity time, whether it's stale, or if a maintainer alert is needed for silent edits). It also provides tool functions for interacting with GitHub, such as adding/removing labels, posting comments, and closing issues. The PROMPT_INSTRUCTION.txt file outlines the detailed decision-making workflow for the AI agent, covering scenarios for active, stale, and pending issues based on user roles and activity. Configuration parameters like thresholds and API limits are managed in config.go, while main.go orchestrates the agent's execution, processing issues concurrently in chunks and logging performance metrics. Utility functions for robust HTTP requests with retries and API call counting are provided in util.go. Review comments primarily focus on improving error handling and consistency: specifically, using safer type assertions for GraphQL error responses, handling potential json.Marshal errors in HTTP requests, correcting a placeholder format in the prompt instruction, changing a log.Printf level from 'FATAL' to 'ERROR', suggesting a more efficient isMaintainer check using a map, and standardizing variable naming conventions for labels.
| if errs, ok := resp["errors"]; ok { | ||
| errList := errs.([]any) | ||
| firstErr := errList[0].(map[string]any) | ||
| return nil, fmt.Errorf("GraphQL Error: %v", firstErr["message"]) |
There was a problem hiding this comment.
The type assertions errs.([]any) and firstErr.(map[string]any) can cause a panic if the resp["errors"] or errList[0] are not of the expected type. It's safer to use the comma ok idiom to handle potential type assertion failures gracefully.
| if errs, ok := resp["errors"]; ok { | |
| errList := errs.([]any) | |
| firstErr := errList[0].(map[string]any) | |
| return nil, fmt.Errorf("GraphQL Error: %v", firstErr["message"]) | |
| if errs, ok := resp["errors"]; ok { | |
| if errList, ok := errs.([]any); ok && len(errList) > 0 { | |
| if firstErr, ok := errList[0].(map[string]any); ok { | |
| return nil, fmt.Errorf("GraphQL Error: %v", firstErr["message"]) | |
| } | |
| } | |
| } |
| func PostRequest(url string, payload any) (any, error) { | ||
| incrementAPICallCount() | ||
|
|
||
| body, _ := json.Marshal(payload) |
There was a problem hiding this comment.
The error returned by json.Marshal(payload) is ignored. If payload cannot be marshaled (e.g., due to unsupported types), this will result in an empty or malformed request body being sent, which can lead to unexpected behavior or API errors. It's important to handle this error.
body, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal POST request payload: %w", err)
}| func PatchRequest(url string, payload any) (any, error) { | ||
| incrementAPICallCount() | ||
|
|
||
| body, _ := json.Marshal(payload) |
There was a problem hiding this comment.
Similar to PostRequest, the error returned by json.Marshal(payload) is ignored here. This can lead to silent failures or malformed PATCH requests if the payload cannot be marshaled correctly. Error handling should be added.
body, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal PATCH request payload: %w", err)
}| - **IF (Question == YES) AND (Time == YES) AND (Internal Discussion Check == FALSE):** | ||
| - **Action**: Call `add_stale_label_and_comment`. | ||
| - **Check**: If '{REQUEST_CLARIFICATION_LABEL}' is not in `current_labels`, call `add_label_to_issue` with '{REQUEST_CLARIFICATION_LABEL}'. | ||
| - **Report**: "Analysis for Issue #[number]: STALE. Maintainer asked question [days_since_activity] days ago. Marking stale." |
There was a problem hiding this comment.
The placeholder [days_since_activity] in the report string is not a standard {}-style placeholder used elsewhere in the prompt. This might lead to the literal string [days_since_activity] being reported instead of the actual value. Please update it to {days_since_activity} for consistency and correct replacement.
- **Report**: "Analysis for Issue #[number]: STALE. Maintainer asked question {days_since_activity} days ago. Marking stale."
| // Uses your util-layer retry + backoff logic | ||
| data, err := GetRequest(url, params) | ||
| if err != nil { | ||
| log.Printf("FATAL: Failed to verify repository maintainers. Error: %v", err) |
There was a problem hiding this comment.
Using "FATAL" in log.Printf is misleading if the program is not actually terminated. Since this function returns an error for the caller to handle, log.Printf is appropriate, but the string "FATAL" should be changed to "ERROR" to accurately reflect that the program continues execution.
| log.Printf("FATAL: Failed to verify repository maintainers. Error: %v", err) | |
| log.Printf("ERROR: Failed to verify repository maintainers. Error: %v", err) |
| // Helper to check if actor is in maintainers list | ||
| func isMaintainer(actor string, maintainers []string) bool { | ||
| for _, m := range maintainers { | ||
| if actor == m { | ||
| return true | ||
| } | ||
| } | ||
| return false |
There was a problem hiding this comment.
The isMaintainer function performs a linear search through the maintainers slice. For a potentially large number of maintainers, this could become inefficient. Consider converting the maintainers slice to a map[string]bool when caching them in getCachedMaintainers for O(1) lookup time, which would improve efficiency.
func isMaintainer(actor string, maintainers map[string]bool) bool {
_, ok := maintainers[actor]
return ok
}| STALE_LABEL_NAME = "stale" | ||
| RequestClarificationLabel = "request clarification" |
There was a problem hiding this comment.
The naming conventions for STALE_LABEL_NAME (SCREAMING_SNAKE_CASE) and RequestClarificationLabel (camelCase) are inconsistent. In Go, it's idiomatic to use camelCase for exported variables. If these are intended to be constants, they should be declared with the const keyword. For consistency, consider making both camelCase if they are variables, or const if they are constants.
| STALE_LABEL_NAME = "stale" | |
| RequestClarificationLabel = "request clarification" | |
| StaleLabelName = "stale" | |
| RequestClarificationLabel = "request clarification" |
|
Why is an agent and LLM needed for this? Why is a rule based system insufficient? It looks like the rules are well defined and I'm pretty sure this is what most people are doing when I have seen it over the years (since before LLMs) |
What this PR does
Introduces a stale issue automation bot implemented in Go.
Applies a clear decision workflow:
Marks issues as stale when maintainers request clarification and no response is received within the threshold.
Removes the stale label when users respond.
Alerts maintainers on silent description edits (ghost edits).
Automatically closes issues that remain stale beyond the close threshold.
Supports concurrent issue processing with rate limiting.
Uses configurable time thresholds and labels via environment variables.