Skip to content

feat: resolve labeled GitLab review discussions - #120

Merged
qiankunli merged 1 commit into
mainfrom
worktree-resolvable-review-comments
Jul 30, 2026
Merged

feat: resolve labeled GitLab review discussions#120
qiankunli merged 1 commit into
mainfrom
worktree-resolvable-review-comments

Conversation

@qiankunli

Copy link
Copy Markdown
Collaborator

Publish GitLab review findings as replyable discussions even when a diff
anchor cannot be created, while keeping the aggregate review summary as a
plain note.

Expose provider-neutral resolution state and resolve a discussion only after
a valid ccr:label reply lands. Resolution remains best-effort so a successful
label is not duplicated when GitLab cannot close the thread.

Update the label-review workflow, regression coverage, and plugin version to
0.2.12.

Publish GitLab review findings as replyable discussions even when a diff
anchor cannot be created, while keeping the aggregate review summary as a
plain note.

Expose provider-neutral resolution state and resolve a discussion only after
a valid ccr:label reply lands. Resolution remains best-effort so a successful
label is not duplicated when GitLab cannot close the thread.

Update the label-review workflow, regression coverage, and plugin version to
0.2.12.
Comment thread devloop/scripts/pr.py
return 1
print(f"replied to {ns.comment_id} on {pr_label(forge.provider, number)}")
suffix = ""
if review_feedback.label_verdict(ns.body) and target.resolvable and not target.resolved:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 devloop code-review · seed-2.1-pro

label_verdict() returns a str, not bool. While this works correctly here because empty strings are falsy and valid verdicts are truthy, it's an implicit coercion that could mislead future readers. The function is used as a boolean predicate but its return type is str. Consider extracting to a local variable for clarity — e.g., verdict = review_feedback.label_verdict(ns.body) then if verdict and target.resolvable ... — which also avoids calling label_verdict twice if the verdict is needed later.

ccr:fp=4f43378fb247

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccr:label=debatable — label_verdict 的返回值契约就是合法 verdict 或空串,此处只调用一次且只需真假判断;提取局部变量属于风格偏好。

def reply(self, number, target, body):
self.replied.append((number, target.id, body))
def resolve_thread(self, number, target):
self.resolved.append((number, target.thread_id))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 devloop code-review · seed-2.1-pro

The _F._FakeForge.resolve_thread stub does not mutate the comment's resolved field, so the second assertion (assert fake.resolved == [(5, "30")]) cannot distinguish between "the guard correctly skipped re-resolution" and "the guard is missing but the fake happens to only record one call." Consider also updating a _resolved tracking dict keyed by comment id so comments() returns the updated resolved state, making the gate in cmd_reply truly exercised.

ccr:fp=0d500987fff2

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccr:label=wrong — 第二次调用拿到的 target 仍明确是 unresolved;若缺少合法 label 条件,普通回复会再次调用 resolve_thread 并让列表出现第二项,现有断言能检出。该用例本就不负责测试 resolved 分支。 #textbook

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 devloop code-review · deepseek-v4-pro

The _FakeForge base class in _testkit.py does not override thread_comment, so it inherits Forge.thread_commit which raises ForgeError. The Refusing test case at line 418-423 implicitly depends on this inherited behavior to keep n2 == 0. If someone later adds a thread_comment override to _FakeForge, the Refusing test would break because thread_comment would now succeed for the 3rd comment ("no path at all") even though diff_comment still fails. Consider making Refusing also explicitly override thread_comment to raise ForgeError, or adding a comment noting the dependency.

ccr:fp=3c419ad54d8f

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccr:label=debatable — Refusing 有意组合“diff anchor 拒绝 + Forge 默认不支持无锚 thread”以覆盖最终回落;未来若公共 FakeForge 新增 thread 支持,测试期望也应随能力变化。显式重写只是隔离测试替身的防御性选择。 #padding

@qiankunli

Copy link
Copy Markdown
Collaborator Author

🤖 devloop code-review · origin/main..HEAD · bede571e7 · models: deepseek-v4-pro×17, seed-2.1-pro×19, seed-2.1-turbo×18 · cost: 149s · ccr v1.9.0

3 finding(s)(3 条已锚到 diff)

@qiankunli
qiankunli merged commit eecd6bb into main Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants