feat: PM-Agent also need to look at PR comments
gate / gate (pull_request) Successful in 2m58s
images / images (pull_request) Successful in 1m6s
gate / conformance (pull_request) Has been skipped

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 revise)
This commit is contained in:
pm-agent committed 2026-11-07 06:02:58 +00:00
1 parent 2fdb95393c
commit e8e9de8611
7 files changed
+471 -189

No files matched your search

+45 -16
View File
@@ -169,16 +169,17 @@ the live Gitea — see `decisions/2026-11-07-pm-agent-pr-mentions.md`.
With it on, the dispatcher runs a second, independent pass each poll. A mention of the bot
account on a **pull request** makes the PM read that pull request — its description, its
changed files, its diff, the thread it was mentioned in and the rest of the conversation —
and **reply in that same thread**. Both places count: the pull request's conversation, and
inline review threads against a file and a line.
and **reply where it was mentioned**. Three places count: the pull request's conversation, a
review's own body (Gitea renders it in the conversation, so the reply goes there), and an
inline review comment against a file and a line.
```
@pm-agent in a PR comment ─▶ discover (notifications, else open PRs)
or an inline review thread │
├─▶ read /issues/{n}/comments, /pulls/{n}/reviews,
or in a review body │
or on a line inline ├─▶ read /issues/{n}/comments, /pulls/{n}/reviews,
│ .../reviews/{id}/comments
├─▶ decide: self-authored? already answered? mentioned,
│ or a follow-up in an already-mentioned thread?
├─▶ decide, per comment: self-authored? already
│ answered? does it name the bot?
├─▶ assemble the PR + diff + thread, every field fenced
│ and labelled UNTRUSTED
├─▶ ask the model: `comment` or `decline`, and nothing else
@@ -191,13 +192,37 @@ ask for work in a shape this program would accept. Nothing on the path calls `bu
The change-authoring half of issue #116 — revise a branch an agent owns, versus open a fresh
branch and PR against `main` for somebody else's — is a separate issue and a separate PR.
**"The same thread."** Inline review comments have a thread identity in Gitea (comments on
the same file and diff position are one conversation). Pull-request conversation comments do
not — they are a flat list — so the issue path's existing convention is reused: the
conversation is **one** thread, and from the first mention in a thread onward, every later
non-bot comment in it continues the conversation without a new mention. A comment made
*before* the mention is not pulled in, and continuation is per thread, not per pull request.
That is a policy choice, not a Gitea fact; it is flagged on issue #116.
**An explicit mention is the only trigger.** A comment is answered if and only if it is not
the bot's own, it spells out `@`-the-bot-account, and no earlier reply of the bot's says it
was already answered. Nothing else is actionable: a comment with no mention produces **no
reply and no model call**, however recently the bot was mentioned or replied nearby. So a
mention is a discrete, self-limiting request — one `@` gets one reply in the place it was
written, and the rest of the pull request is read as *context* rather than as a queue of
questions.
That is the operator's decision on issue #116, taken once it was established that pull-request
conversation comments are a flat list rather than a hierarchy: *"since there is no threads and
the PR comments are flat, then the pm-agent should be looking for explicit mentions rather than
a single mention and continued scrapping of comments within the PR in perpetuity"*. It replaced
an earlier rule where the first mention in a thread armed replies to every later non-bot
comment in it. It is applied uniformly, including to inline review comments — those *are*
genuinely threaded in Gitea, but keeping continuation for them alone would be a second rule
nobody asked for. The fixtures that used to assert continuation are **inverted, not deleted**,
so the old behaviour cannot creep back unnoticed.
Two practical consequences. A mention is **repeatable**: ask again by tagging again, and each
tag gets its own reply. And a mention is **not a conversation** — the PM is told to answer as
if it is the last word it will see, and to ask you to tag it again if it needs something back.
What remains, unavoidably, is that *anyone* who writes the handle gets a reply — the operator's
answer (3) is "anyone can talk and have the agent work" — and Gitea cannot tell a quoted
mention (`> @pm-agent what do you think?`) from a fresh one, so re-quoting an old mention in a
new comment costs one more reply.
**Thread identity is now only about where the reply goes.** Inline review comments are grouped
by (path, position), which is Gitea's own grouping and is what makes the reply land in the
thread that was spoken to rather than beside it; conversation comments and review bodies are
answered in the conversation. None of that is part of the trigger any more.
**Loop prevention.** There is no author gate — anyone who can comment can start a run, which
is the operator's decision (issue #116, answer 3: *"anyone can talk and have the agent work.
@@ -223,9 +248,13 @@ bot's *own* comments — pasting it into yours does not silence a mention.
The operator asked for "a way to check for '@'s for the bot account instead of scouring the
entire pull request list and associated threads". That is Gitea's notification list: a
mention subscribes the mentioned account to the thread, and the subscription then raises a
notification for **every later comment on it** — which answers discovery and thread
continuation with one bounded call.
mention subscribes the mentioned account to the thread, so one bounded call names the pull
requests worth reading.
It is a **hint, never the decision** — the trigger is re-derived by reading that pull
request's comments. That split is why the endpoint over-delivering is harmless: the
subscription keeps raising notifications for later comments on a pull request the bot was
once mentioned on, and since those comments carry no mention, they resolve to nothing.
**It will be refused on today's token.** `/api/v1/notifications` sits behind Gitea's
`notification` scope category, and the bot's token is minted