Merge remote-tracking branch 'origin/main' into Metrics-and-Logs

This commit is contained in:
Robin Olsen
2026-03-09 13:24:07 +01:00
44 changed files with 1949 additions and 477 deletions

View File

@@ -2,6 +2,7 @@ package tasks
import (
"context"
"strings"
"github.com/LazyBachelor/LazyPM/internal/models"
"github.com/LazyBachelor/LazyPM/internal/utils/check"
@@ -12,12 +13,12 @@ const backlogRefinementDescription = `You are tasked with backlog refinement.
The product backlog has become cluttered with old and unclear issues. You need to groom the backlog:
1. Review all issues in the backlog
2. Identify stale or obsolete issues (older items that are no longer relevant)
3. Update issue descriptions for clarity where needed
4. Close issues that are duplicates or no longer applicable
5. Reprioritize issues based on current business value
6. Ensure remaining issues are well-defined and actionable
1. Go to the backlog.
2. Find two issues that got the same name or describe the same problem.
3. Open one of these issues.
4. Select "Close issue"
5. Choose "Duplicate issue" as closing reason.
6. Save/close issue.
Focus on making the backlog a reliable source of upcoming work.`
@@ -47,60 +48,65 @@ func (t *BacklogRefinementTask) Questions(interfaceType InterfaceType) Questions
return BaseQuestions(interfaceType).With(
huh.NewGroup(
huh.NewSelect[int]().
Title("How many issues did you close or update during refinement?").
Key("how-many-duplicate-issues").
Title("How many duplicate issues did you close during refinement?").
Options(
huh.NewOption("1-2", 1),
huh.NewOption("3-4", 2),
huh.NewOption("5+", 3),
huh.NewOption("1", 1),
huh.NewOption("2", 2),
huh.NewOption("3+", 3),
),
),
)
}
func (t *BacklogRefinementTask) QuestionnaireKeys(_ InterfaceType) []string {
return []string{"task_completed", "task_difficulty", "how-many-duplicate-issues"}
}
func (t *BacklogRefinementTask) Setup(ctx context.Context) error {
if err := ClearIssues(t.app); err != nil {
return err
}
refinementIssues := []*models.Issue{
NewIssueBuilder().
WithTitle("Old feature request: Fax integration").
WithDescription("Allow sending reports via fax. DEPRECATED - nobody uses fax anymore").
WithPriority(3).
WithStatus(models.StatusOpen).
WithIssueType(models.TypeTask).
Build(),
NewIssueBuilder().
WithTitle("User profile page").
WithDescription("Create page for users to view profile. DUPLICATE of user-management epic").
WithDescription("Create page for users to view and edit their profile").
WithPriority(2).
WithStatus(models.StatusOpen).
WithIssueType(models.TypeTask).
Build(),
NewIssueBuilder().
WithTitle("Mobile app redesign").
WithDescription("Redesign mobile interface with modern UI patterns. Still relevant, needs clarity").
WithTitle("User profile page").
WithDescription("Allow users to view their profile information").
WithPriority(2).
WithStatus(models.StatusOpen).
WithIssueType(models.TypeTask).
Build(),
NewIssueBuilder().
WithTitle("Fix login timeout").
WithDescription("Login sometimes times out after 30 seconds").
WithPriority(1).
WithStatus(models.StatusOpen).
WithIssueType(models.TypeTask).
WithIssueType(models.TypeBug).
Build(),
NewIssueBuilder().
WithTitle("Legacy data export tool").
WithDescription("Tool for exporting data in old format. OBSOLETE - format no longer supported").
WithPriority(3).
WithTitle("Fix login timeout").
WithDescription("Users report login requests timing out").
WithPriority(1).
WithStatus(models.StatusOpen).
WithIssueType(models.TypeTask).
WithIssueType(models.TypeBug).
Build(),
NewIssueBuilder().
WithTitle("API v1 documentation").
WithDescription("Document old API version. DEPRECATED - migrating to v2").
WithPriority(3).
WithTitle("Mobile app redesign").
WithDescription("Redesign mobile interface with modern UI patterns").
WithPriority(2).
WithStatus(models.StatusOpen).
WithIssueType(models.TypeTask).
Build(),
NewIssueBuilder().
WithTitle("Customer feedback system").
WithDescription("Build system for collecting user feedback. HIGH VALUE - prioritize").
WithDescription("Build system for collecting user feedback").
WithPriority(2).
WithStatus(models.StatusOpen).
WithIssueType(models.TypeTask).
@@ -122,5 +128,22 @@ func (t *BacklogRefinementTask) Setup(ctx context.Context) error {
func (t *BacklogRefinementTask) Validate(ctx context.Context) ValidationFeedback {
expect := check.NewExpector()
issues, err := FetchIssues(ctx, t.app, t.setupIssue)
if err != nil {
return expect.ValidationFeedback
}
var closedDuplicate *models.Issue
for _, issue := range issues {
if issue.Status == models.StatusClosed &&
strings.Contains(strings.ToLower(issue.CloseReason), "duplicate") {
closedDuplicate = issue
break
}
}
expect.Assert(closedDuplicate != nil,
"Expected one duplicate issue to be closed with 'Duplicate issue' as closing reason")
return expect.Complete()
}

View File

@@ -2,7 +2,6 @@ package tasks
import (
"context"
"fmt"
"os"
"strings"
@@ -18,15 +17,14 @@ The MongoDB Driver dependency in the file is outdated and needs to be updated to
This is a common task for developers, and it requires attention to detail and the ability to follow instructions carefully.
Your task:
1. Assign this Issue to yourself as "Me" and mark it as "In Progress"
2. Create a New Issue and give it these details:
1. Create a New Issue and give it these details:
- Title: "Upgrade MongoDB Driver Dependency"
- Description: "We need to upgrade the MongoDB Driver dependency to the latest version."
- Status: "In Progress"
- Issue Type: "Chore"
3. A file will appear in the current directory named "code.txt".
2. A file will appear in the current directory named "code.txt".
Open it and follow the instructions inside. And save the file after you are done.
4. When you are done, mark this and the issue you made as "Closed".`
3. When you are done, mark this and the issue you made as "Closed".`
var textFileDescription = `
Please upgrade the MongoDB Driver dependency in the go.mod file to the latest version.
@@ -122,7 +120,7 @@ func (t *CodingTask) Setup(ctx context.Context) error {
return nil
}
var codeTaskInProgress = false
var codingTaskInProgress = false
func (t *CodingTask) Validate(ctx context.Context) ValidationFeedback {
expect := check.NewExpector()
@@ -132,47 +130,31 @@ func (t *CodingTask) Validate(ctx context.Context) ValidationFeedback {
return expect.ValidationFeedback
}
expect.Assert(t.setupIssue.Assignee == "Me",
"The original issue should be assigned to 'Me'.")
expect.Assert(t.setupIssue.Status == models.StatusInProgress,
"The original issue should be marked as In Progress before starting work.")
expect.Assert(len(issues) > 0,
"No new issues created. Please create an issue with the specified details.")
if len(issues) == 0 {
expect.Fail("No new issues created")
return expect.ValidationFeedback
} else {
expect.Pass("An issue was created")
}
issue := issues[0]
expect.Assert(len(issues) < 2,
"Multiple issues were created instead of one. Delete the extra issues and try again.")
expect.Assert(len(issues) < 2, "Multiple issues were created instead of one")
expect.NotEmptyString(issue.Title,
"Issue title should not be empty")
expect.NotEmptyAndEqual(issue.Title, "Upgrade MongoDB Driver Dependency", "Issue title")
expect.Assert(issue.Title == "Upgrade MongoDB Driver Dependency",
fmt.Sprintf("Issue title does not match the expected value 'Upgrade MongoDB Driver Dependency', but was '%s'", issue.Title))
expect.NotEmptyAndEqual(issue.Description,
"We need to upgrade the MongoDB Driver dependency to the latest version.", "Issue description")
expect.NotEmptyString(issue.Description,
"Issue description should not be empty")
expect.NotEmptyAndEqual(issue.Assignee, "Me", "Issue Assignee")
expect.Assert(issue.Description == "We need to upgrade the MongoDB Driver dependency to the latest version.",
fmt.Sprintf("Issue description does not match 'We need to upgrade the MongoDB Driver dependency to the latest version.', but was '%s'", issue.Description))
expect.Equal(issue.IssueType, models.TypeChore, "Issue type")
expect.Assert(issue.IssueType == models.TypeChore,
fmt.Sprintf("Issue type should be 'Chore', but was '%s'", issue.IssueType))
expect.Assert(issue.Assignee == "Me",
fmt.Sprintf("Issue should be assigned to 'Me', but was assigned to '%s'", issue.Assignee))
if issue.Status == models.StatusInProgress || codeTaskInProgress {
codeTaskInProgress = true
if issue.Status == models.StatusInProgress || codingTaskInProgress {
codingTaskInProgress = true
} else {
expect.Fail("Issue should be marked as In Progress when work starts")
expect.Fail("The issue should be marked as In Progress while working on the task.")
return expect.ValidationFeedback
}
if _, err := os.Stat("./code.txt"); os.IsNotExist(err) {
@@ -195,14 +177,8 @@ func (t *CodingTask) Validate(ctx context.Context) ValidationFeedback {
expect.Assert(strings.Contains(code, "go.mongodb.org/mongo-driver v1.17.9"),
"The MongoDB Driver dependency should be updated to version v1.17.9 in the file.")
if !codeTaskInProgress {
return expect.ValidationFeedback
} else if issue.Status != models.StatusClosed {
expect.Fail("Issue should be set to Closed once the work is completed")
} else {
expect.Assert(t.setupIssue.Status == models.StatusClosed,
"The original setup issue should be set to Closed once the work is completed")
}
expect.Assert(codingTaskInProgress && issue.Status == models.StatusClosed,
"The issue should be marked as Closed after completing the task.")
return expect.Complete()
}

View File

@@ -16,8 +16,7 @@ Your task:
1. Create a new issue with the title "My first Issue"
2. Add this detailed description "I need to do some coding"
3. Assign the issue to yourself as "Me"
4. Mark the issue as In Progress when you start working on it
5. Close the issue once you've completed the work
4. Mark the issue as In Progress when you are done.
Make sure to fill out all the necessary details to help others understand the work item.`
@@ -43,8 +42,7 @@ func (t *CreateIssueTask) Questions(interfaceType InterfaceType) Questions {
return BaseQuestions(interfaceType)
}
func (t *CreateIssueTask) QuestionnaireKeys(interfaceType InterfaceType) []string {
_ = interfaceType
func (t *CreateIssueTask) QuestionnaireKeys(_ InterfaceType) []string {
return []string{"task_completed", "task_difficulty"}
}
@@ -61,8 +59,6 @@ func (t *CreateIssueTask) Setup(ctx context.Context) error {
return t.app.Issues.CreateIssue(ctx, t.setupIssue, "")
}
var isInProgress = false
func (t *CreateIssueTask) Validate(ctx context.Context) ValidationFeedback {
expect := check.NewExpector()
@@ -78,30 +74,14 @@ func (t *CreateIssueTask) Validate(ctx context.Context) ValidationFeedback {
issue := issues[0]
expect.Assert(len(issues) < 2, "Multiple issues were created instead of one. Delete the extra issues and try again.")
expect.Assert(len(issues) < 2, "Multiple issues were created instead of one")
expect.NotEmptyString(issue.Title, "Issue title should not be empty")
expect.Assert(issue.Title == "My first Issue",
fmt.Sprintf("Issue title does not match the expected value 'My first Issue', but was '%s'", issue.Title))
expect.NotEmptyAndEqual(issue.Title, "My first Issue", "Issue title")
expect.NotEmptyAndEqual(issue.Description, "I need to do some coding", "Issue description")
expect.NotEmptyAndEqual(issue.Assignee, "Me", "Issue assignee")
expect.NotEmptyString(issue.Description, "Issue description should not be empty")
expect.Assert(issue.Description == "I need to do some coding",
fmt.Sprintf("Issue description does not match the expected value 'I need to do some coding', but was '%s'", issue.Description))
expect.Assert(issue.Assignee == "Me",
fmt.Sprintf("Issue should be assigned to 'Me', but was assigned to '%s'", issue.Assignee))
if issue.Status == models.StatusInProgress || isInProgress {
isInProgress = true
} else {
expect.Fail("Issue should be marked as in-progress when work starts")
}
if !isInProgress {
return expect.ValidationFeedback
} else if issue.Status != models.StatusClosed {
expect.Fail("Issue should be set to Closed once the work is completed")
}
expect.Assert(issue.Status == models.StatusInProgress,
fmt.Sprintf("Issue status should be 'In Progress', but was '%s'", issue.Status))
return expect.Complete()
}