From d89caf4af2f5faa1a3bdc6e3940e6a696d9f19a5 Mon Sep 17 00:00:00 2001 From: ceo Date: Mon, 14 Sep 2026 08:04:37 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9Astalled=20=E5=88=86=E9=A1=9E?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=20agents#44=E2=80=94=E2=80=94=E5=AD=90?= =?UTF-8?q?=E9=A0=85=E5=85=A8=E9=97=9C=E9=96=89=E7=9A=84=E7=88=B6=E8=BF=BD?= =?UTF-8?q?=E8=B9=A4=E9=A0=85=E4=B8=80=E5=BE=8B=E6=A8=99=20parent-tracking?= =?UTF-8?q?=EF=BC=88#47=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit considerIssue 原先僅在 anyOpen(children) 時標 parent-tracking,子項全關閉時 退回通用原因(如 assignee-idle),與 gitea.py(agents#44 修正)不一致, 導致巡邏「父項收尾」規則永不觸發。改為 children 非空一律 parent-tracking, 並移除已無引用的 anyOpen。 回歸測試 TestScanStalledParentWrapup 補子項全關閉 fixture; fakeSource.OpenIssues 過濾 closed 以對齊真實 API 語義。 --- internal/workflow/workflow.go | 15 ++++------- internal/workflow/workflow_test.go | 43 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/internal/workflow/workflow.go b/internal/workflow/workflow.go index 8ca8f59..a0ece73 100644 --- a/internal/workflow/workflow.go +++ b/internal/workflow/workflow.go @@ -530,7 +530,11 @@ func considerIssue(src Source, org, repo string, issue Issue, comments []Comment case lastCommentBy == "": reason = "no-commenter" } - if len(children) > 0 && anyOpen(children) { + if len(children) > 0 { + // 父追蹤項一律標 parent-tracking(agents #44,與 gitea.py 同步): + // 有 open 子項在推進時,父項留言閒置屬等待子項;子項全關閉時只剩 + // 父項收尾(回報驗收並關閉),兩者都不是 assignee 停滯。統一標記 + // 讓「父項收尾」規則能正確匹配,不退回通用原因(如 assignee-idle)。 reason = "parent-tracking" } item := Item{ @@ -606,15 +610,6 @@ func considerPull(src Source, org, repo string, pr PullRequest, comments []Comme return item, true } -func anyOpen(children []Child) bool { - for _, c := range children { - if c.State == "open" { - return true - } - } - return false -} - // round1 舍入到小數一位(對應 Python round(x, 1))。 // 經 strconv 的十進位正確舍入(半偶數),與 Python 的浮點 round 行為一致, // 避免 (x*10+0.5) 截斷在 .x5 邊界產生偏差。 diff --git a/internal/workflow/workflow_test.go b/internal/workflow/workflow_test.go index 81ae603..da3ea92 100644 --- a/internal/workflow/workflow_test.go +++ b/internal/workflow/workflow_test.go @@ -42,6 +42,9 @@ func (f *fakeSource) OrganizationRepositories(org string) ([]Repo, error) { func (f *fakeSource) OpenIssues(owner, repo string) ([]Issue, error) { var out []Issue for _, i := range f.issues[owner+"/"+repo] { + if i.State == "closed" { + continue // 對齊真實 API:OpenIssues 只回 open;closed 條目僅供 TryGetIssue 解引用。 + } out = append(out, i) } // 對齊真實 API:issues 依 id(=number)遞增回傳,消除 map 迭代隨機性。 @@ -245,6 +248,46 @@ func TestScanStalledNudgeCooldown(t *testing.T) { } } +// TestScanStalledParentWrapup(teai #47,同步 agents #44):子項全關閉的父追蹤項 +// 不退回通用原因(assignee-idle),一律標 parent-tracking——「父項收尾」情境。 +func TestScanStalledParentWrapup(t *testing.T) { + now := at(0) + // 父項 3 最後留言來自 assignee ceo(舊行為會誤標 assignee-idle); + // 唯一子項 7 已關閉,父子最後活動皆 5 小時前。 + src := &fakeSource{ + orgs: []string{"alterminal"}, + repos: map[string][]Repo{"alterminal": repoWith("teai", true)}, + issues: map[string]map[int64]Issue{ + "alterminal/teai": { + 3: {Number: 3, Title: "父", State: "open", Body: "子項:#7", CreatedAt: iso(-700), UpdatedAt: iso(-300), Assignees: []User{{UserName: "ceo"}}, Repository: &Repository{FullName: "alterminal/teai"}}, + 7: {Number: 7, Title: "子", State: "closed", CreatedAt: iso(-650), UpdatedAt: iso(-300), Repository: &Repository{FullName: "alterminal/teai"}}, + }, + }, + comments: map[string][]Comment{ + "alterminal/teai/3": {{User: &User{UserName: "ceo"}, CreatedAt: iso(-300)}}, + }, + } + got, err := ScanStalled(src, "admin", 4.0, now) + if err != nil { + t.Fatalf("ScanStalled: %v", err) + } + var parent *Item + for i := range got { + if got[i].Number == 3 { + parent = &got[i] + } + } + if parent == nil { + t.Fatalf("父項 3 應列入(5h 無活動),got %v", got) + } + if parent.Reason != "parent-tracking" { + t.Errorf("子項全關閉的父項 reason = %q, want parent-tracking", parent.Reason) + } + if len(parent.ChildActivity) == 0 { + t.Errorf("父項 3 應附 child_activity,got %v", parent.ChildActivity) + } +} + // TestScanStalledParentTracking 父追蹤項:子項活動計入 last_activity、reason 標記。 func TestScanStalledParentTracking(t *testing.T) { now := at(0) -- 2.54.0