feat: PM-Agent also need to look at PR comments
From Gitea issue #116. Authored by the PM agent's worker; the merge gate is the approver. Refs #116. Agent: pm-agent (issue #116, mode implement)
This commit is contained in:
1 parent
5b484f6b78
commit
2fdb95393c
9 files changed
+2055
-32
No files matched your search
+536
@@ -0,0 +1,536 @@
|
||||
{
|
||||
"_what_this_is": [
|
||||
"Fixtures for the pull-request-mention trigger in infra/agent/pm-dispatch.py, driven by",
|
||||
"`PM_SELFTEST=1 pm-dispatch` (and therefore by the Containerfile's verify stage and by",
|
||||
"infra/agent/build-image.sh). Each case feeds normalize_pr_entries() + plan_pr_replies()",
|
||||
"and asserts the EXACT set of threads that would be answered, with which comment keys.",
|
||||
"",
|
||||
"HOW HONEST THESE ARE, stated plainly because it matters for what a green selftest",
|
||||
"proves. These objects are written to the field names and types in Gitea's published",
|
||||
"swagger for the 1.24 line (Comment, PullReview, PullReviewComment, NotificationThread).",
|
||||
"They were NOT recorded from a live server: the branch that wrote them held no Gitea",
|
||||
"credential. So a passing case proves that this program's PARSING and TRIGGER LOGIC are",
|
||||
"right for that shape -- the loop guard, the idempotency skip, the continuation policy --",
|
||||
"and proves nothing about whether Gitea emits that shape. Only unused-and-wrong field",
|
||||
"names can hide here, and the remedy is cheap: capture one real response with the curl",
|
||||
"commands in platform/pm-agent/README.md and replace the objects below with it. Fields",
|
||||
"this program never reads are kept anyway, so the diff against a real capture is small.",
|
||||
"",
|
||||
"Only the fields the dispatcher reads are load-bearing:",
|
||||
" Comment ............. id, user.login, created_at, body",
|
||||
" PullReview .......... id, user.login, submitted_at, body, comments_count",
|
||||
" PullReviewComment ... id, user.login, created_at, body, path, position",
|
||||
"",
|
||||
"`expect.threads` is in the order plan_pr_replies returns: NEWEST-ANCHORED FIRST.",
|
||||
"",
|
||||
"THE CONTINUATION POLICY THESE PIN. A thread becomes a conversation the PM is part of",
|
||||
"at the first mention in it (or at a marker in one of the PM's own comments), and every",
|
||||
"later non-bot comment in that thread then triggers without a new mention -- the",
|
||||
"operator's wording on issue #116, answer (4'). Cases (i) and (o) are the two edges:",
|
||||
"comments AFTER the mention are in, comments BEFORE it are not. Case (l) pins that this",
|
||||
"is per thread, not per pull request."
|
||||
],
|
||||
"bot_login": "pm-agent",
|
||||
"cases": [
|
||||
{
|
||||
"name": "(a) a mention in a PR conversation comment is detected",
|
||||
"pull": 120,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9001,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T09:00:00Z",
|
||||
"updated_at": "2026-11-07T09:00:00Z",
|
||||
"body": "@pm-agent can you look at the retry loop in cycle() here? I think it double-counts."
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{"thread": "conversation", "reason": "mention", "keys": ["comment-9001"]}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(b) a mention in an inline review comment is detected, as its own thread",
|
||||
"pull": 121,
|
||||
"comments": [],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 400,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T09:10:00Z",
|
||||
"comments_count": 1
|
||||
}
|
||||
],
|
||||
"review_comments": {
|
||||
"400": [
|
||||
{
|
||||
"id": 7701,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T09:10:00Z",
|
||||
"body": "@pm-agent this cap looks off by one.",
|
||||
"path": "infra/agent/pm-dispatch.py",
|
||||
"position": 42,
|
||||
"original_position": 42,
|
||||
"pull_request_review_id": 400,
|
||||
"diff_hunk": "@@ -1,3 +1,4 @@"
|
||||
}
|
||||
]
|
||||
},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{
|
||||
"thread": "inline:infra/agent/pm-dispatch.py@42",
|
||||
"reason": "mention",
|
||||
"keys": ["review-comment-7701"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(c) a comment authored by the bot itself is ignored, mention and all",
|
||||
"pull": 122,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9010,
|
||||
"user": {"login": "pm-agent", "id": 7},
|
||||
"created_at": "2026-11-07T09:20:00Z",
|
||||
"body": "Worth a look: @pm-agent should re-read this once the gate is green."
|
||||
},
|
||||
{
|
||||
"id": 9011,
|
||||
"user": {"login": "pm-agent", "id": 7},
|
||||
"created_at": "2026-11-07T09:21:00Z",
|
||||
"body": "A plain status note from the agent, no mention at all."
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {"threads": []}
|
||||
},
|
||||
{
|
||||
"name": "(d) an already-processed comment id is skipped",
|
||||
"pull": 123,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9020,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T09:30:00Z",
|
||||
"body": "@pm-agent what does the approval stage need here?"
|
||||
},
|
||||
{
|
||||
"id": 9021,
|
||||
"user": {"login": "pm-agent", "id": 7},
|
||||
"created_at": "2026-11-07T09:31:00Z",
|
||||
"body": "It needs a review from someone who is not the author.\n\n_Posted by the PM agent._\n\n<!-- pm-agent:replied-to=comment-9020 -->"
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {"threads": []}
|
||||
},
|
||||
{
|
||||
"name": "(e) a follow-up with NO mention continues a thread the bot has replied in",
|
||||
"pull": 124,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9030,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T09:40:00Z",
|
||||
"body": "@pm-agent is this safe to merge on a green gate?"
|
||||
},
|
||||
{
|
||||
"id": 9031,
|
||||
"user": {"login": "pm-agent", "id": 7},
|
||||
"created_at": "2026-11-07T09:41:00Z",
|
||||
"body": "Yes, nothing here is a sensitive path.\n\n<!-- pm-agent:replied-to=comment-9030 -->"
|
||||
},
|
||||
{
|
||||
"id": 9032,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T09:45:00Z",
|
||||
"body": "And the conformance gap? No mention in this one on purpose."
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{"thread": "conversation", "reason": "continuation", "keys": ["comment-9032"]}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(f) an unrelated comment on an unrelated PR triggers nothing",
|
||||
"pull": 125,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9040,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T09:50:00Z",
|
||||
"body": "Rebased onto main; the cilium bump is in."
|
||||
},
|
||||
{
|
||||
"id": 9041,
|
||||
"user": {"login": "renovate", "id": 9},
|
||||
"created_at": "2026-11-07T09:51:00Z",
|
||||
"body": "Edited/Blocked: this PR is edited, Renovate will not update it."
|
||||
}
|
||||
],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 401,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "Looks fine to me.",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T09:52:00Z",
|
||||
"comments_count": 0
|
||||
}
|
||||
],
|
||||
"review_comments": {},
|
||||
"expect": {"threads": []}
|
||||
},
|
||||
{
|
||||
"name": "(g) a near-miss handle is not a mention: @pm-agent-worker and an email-like form",
|
||||
"pull": 126,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9050,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T10:00:00Z",
|
||||
"body": "The Job runs as @pm-agent-worker, and the log line was mailed to [email protected]. Also pm-agent without the at-sign."
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {"threads": []}
|
||||
},
|
||||
{
|
||||
"name": "(h) a marker pasted by someone who is NOT the bot does not suppress a mention",
|
||||
"pull": 127,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9060,
|
||||
"user": {"login": "drive-by", "id": 4},
|
||||
"created_at": "2026-11-07T10:10:00Z",
|
||||
"body": "@pm-agent read this.\n\n<!-- pm-agent:replied-to=comment-9060 -->"
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{"thread": "conversation", "reason": "mention", "keys": ["comment-9060"]}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(i) a burst of three unanswered comments is ONE reply covering all three keys",
|
||||
"pull": 128,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9070,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T10:20:00Z",
|
||||
"body": "@pm-agent one question:"
|
||||
},
|
||||
{
|
||||
"id": 9071,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T10:20:30Z",
|
||||
"body": "actually two."
|
||||
},
|
||||
{
|
||||
"id": 9072,
|
||||
"user": {"login": "someone-else", "id": 5},
|
||||
"created_at": "2026-11-07T10:21:00Z",
|
||||
"body": "three, sorry."
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{
|
||||
"thread": "conversation",
|
||||
"reason": "mention",
|
||||
"keys": ["comment-9070", "comment-9071", "comment-9072"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(j) a mention in a review BODY is a conversation trigger, not an inline one",
|
||||
"pull": 129,
|
||||
"comments": [],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 402,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "@pm-agent summarise what this changes, please.",
|
||||
"state": "REQUEST_CHANGES",
|
||||
"submitted_at": "2026-11-07T10:30:00Z",
|
||||
"comments_count": 0
|
||||
}
|
||||
],
|
||||
"review_comments": {},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{"thread": "conversation", "reason": "mention", "keys": ["review-402"]}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(k) two inline threads on one file are separate threads; the newest anchor comes first",
|
||||
"pull": 130,
|
||||
"comments": [],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 403,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T10:40:00Z",
|
||||
"comments_count": 2
|
||||
}
|
||||
],
|
||||
"review_comments": {
|
||||
"403": [
|
||||
{
|
||||
"id": 7710,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T10:40:00Z",
|
||||
"body": "@pm-agent why this order?",
|
||||
"path": "ci/gate.sh",
|
||||
"position": 10,
|
||||
"pull_request_review_id": 403
|
||||
},
|
||||
{
|
||||
"id": 7711,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T10:41:00Z",
|
||||
"body": "@pm-agent and this skip?",
|
||||
"path": "ci/gate.sh",
|
||||
"position": 55,
|
||||
"pull_request_review_id": 403
|
||||
}
|
||||
]
|
||||
},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{"thread": "inline:ci/gate.sh@55", "reason": "mention", "keys": ["review-comment-7711"]},
|
||||
{"thread": "inline:ci/gate.sh@10", "reason": "mention", "keys": ["review-comment-7710"]}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(l) continuation is PER THREAD: a reply in the conversation does not open the inline thread",
|
||||
"pull": 131,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9080,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T10:50:00Z",
|
||||
"body": "@pm-agent have a look."
|
||||
},
|
||||
{
|
||||
"id": 9081,
|
||||
"user": {"login": "pm-agent", "id": 7},
|
||||
"created_at": "2026-11-07T10:51:00Z",
|
||||
"body": "Looked.\n\n<!-- pm-agent:replied-to=comment-9080 -->"
|
||||
}
|
||||
],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 404,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T10:55:00Z",
|
||||
"comments_count": 1
|
||||
}
|
||||
],
|
||||
"review_comments": {
|
||||
"404": [
|
||||
{
|
||||
"id": 7720,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T10:55:00Z",
|
||||
"body": "no mention here, and the bot has never replied in THIS thread",
|
||||
"path": "ci/lint.sh",
|
||||
"position": 3,
|
||||
"pull_request_review_id": 404
|
||||
}
|
||||
]
|
||||
},
|
||||
"expect": {"threads": []}
|
||||
},
|
||||
{
|
||||
"name": "(m) an inline thread the bot has replied in continues without a mention",
|
||||
"pull": 132,
|
||||
"comments": [],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 405,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T11:00:00Z",
|
||||
"comments_count": 1
|
||||
},
|
||||
{
|
||||
"id": 406,
|
||||
"user": {"login": "pm-agent", "id": 7},
|
||||
"body": "",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T11:01:00Z",
|
||||
"comments_count": 1
|
||||
},
|
||||
{
|
||||
"id": 407,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T11:05:00Z",
|
||||
"comments_count": 1
|
||||
}
|
||||
],
|
||||
"review_comments": {
|
||||
"405": [
|
||||
{
|
||||
"id": 7730,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T11:00:00Z",
|
||||
"body": "@pm-agent is this cap right?",
|
||||
"path": "platform/pm-agent/configmap.yaml",
|
||||
"position": 88,
|
||||
"pull_request_review_id": 405
|
||||
}
|
||||
],
|
||||
"406": [
|
||||
{
|
||||
"id": 7731,
|
||||
"user": {"login": "pm-agent", "id": 7},
|
||||
"created_at": "2026-11-07T11:01:00Z",
|
||||
"body": "It matches the admission policy.\n\n<!-- pm-agent:replied-to=review-comment-7730 -->",
|
||||
"path": "platform/pm-agent/configmap.yaml",
|
||||
"position": 88,
|
||||
"pull_request_review_id": 406
|
||||
}
|
||||
],
|
||||
"407": [
|
||||
{
|
||||
"id": 7732,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T11:05:00Z",
|
||||
"body": "and the worker cap?",
|
||||
"path": "platform/pm-agent/configmap.yaml",
|
||||
"position": 88,
|
||||
"pull_request_review_id": 407
|
||||
}
|
||||
]
|
||||
},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{
|
||||
"thread": "inline:platform/pm-agent/configmap.yaml@88",
|
||||
"reason": "continuation",
|
||||
"keys": ["review-comment-7732"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(o) a comment made BEFORE the mention is not pulled into the conversation",
|
||||
"pull": 134,
|
||||
"comments": [
|
||||
{
|
||||
"id": 9090,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T11:20:00Z",
|
||||
"body": "Opening this for review."
|
||||
},
|
||||
{
|
||||
"id": 9091,
|
||||
"user": {"login": "someone-else", "id": 5},
|
||||
"created_at": "2026-11-07T11:21:00Z",
|
||||
"body": "LGTM."
|
||||
},
|
||||
{
|
||||
"id": 9092,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T11:22:00Z",
|
||||
"body": "@pm-agent what do you think?"
|
||||
}
|
||||
],
|
||||
"reviews": [],
|
||||
"review_comments": {},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{"thread": "conversation", "reason": "mention", "keys": ["comment-9092"]}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(n) a position-less inline comment still lands in a stable thread (review-id fallback)",
|
||||
"pull": 133,
|
||||
"comments": [],
|
||||
"reviews": [
|
||||
{
|
||||
"id": 408,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"body": "",
|
||||
"state": "COMMENT",
|
||||
"submitted_at": "2026-11-07T11:10:00Z",
|
||||
"comments_count": 1
|
||||
}
|
||||
],
|
||||
"review_comments": {
|
||||
"408": [
|
||||
{
|
||||
"id": 7740,
|
||||
"user": {"login": "jakman85", "id": 1},
|
||||
"created_at": "2026-11-07T11:10:00Z",
|
||||
"body": "@pm-agent the whole file, not a line.",
|
||||
"path": "runbooks/pm-agent.md",
|
||||
"pull_request_review_id": 408
|
||||
}
|
||||
]
|
||||
},
|
||||
"expect": {
|
||||
"threads": [
|
||||
{
|
||||
"thread": "inline:runbooks/pm-agent.md@review-408",
|
||||
"reason": "mention",
|
||||
"keys": ["review-comment-7740"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"notification_subjects": [
|
||||
{
|
||||
"_note": "NotificationThread.subject shapes discover_pr_candidates must resolve to an index.",
|
||||
"url": "http://gitea.gitea.svc.cluster.local:3000/api/v1/repos/Home-Network/home-network-k3s-impl/issues/120",
|
||||
"expect_index": 120
|
||||
},
|
||||
{
|
||||
"url": "http://gitea.gitea.svc.cluster.local:3000/api/v1/repos/Home-Network/home-network-k3s-impl/pulls/121",
|
||||
"expect_index": 121
|
||||
},
|
||||
{
|
||||
"_note": "A comment-anchored URL must still yield the PR index, never the comment id.",
|
||||
"url": "http://gitea.gitea.svc.cluster.local:3000/api/v1/repos/Home-Network/home-network-k3s-impl/issues/122/comments/9001",
|
||||
"expect_index": 122
|
||||
},
|
||||
{
|
||||
"_note": "Nothing resolvable: discovery must skip it rather than guess.",
|
||||
"url": "http://gitea.gitea.svc.cluster.local:3000/api/v1/repos/Home-Network/home-network-k3s-impl",
|
||||
"expect_index": null
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user