Files
werkregie/doc/prs/2026-10-12-PR#48-watch-pr.md
claude-code 12f5b86950
werkator build succeeded after 01:15
A coding agent watches its pull request until it must act (#48)
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-12 16:57:01 +02:00

153 lines
7.0 KiB
Markdown

# PR#48: A Coding Agent Watches Its Pull Request Until It Must Act
> **WARNING:** This document describes only the change applied in this PR.
> It may already be outdated once the next PR is merged.
> Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
## Related Links
- [PR#44](https://git.javagil.de/mi/werkregie/pulls/44), whose conflict with `main` stayed unnoticed.
- [PR#43](https://git.javagil.de/mi/werkregie/pulls/43), which shows the human a conflict that an agent leaves.
## Problem
On 2026-10-12, PR#44 waited for its merge while PR#41, PR#43 and PR#46 merged into `main`.
Its coding agent watched only for the merge and for red statuses, not for Gitea's `mergeable`;
the human found the conflict.
Each agent builds such a watch loop of its own, and each one misses something else.
The shared workflow also does not say that a conflict is the agent's to fix,
nor that the version is the next free one.
## Non-Goals
- No watch after the session of the agent ends; "Du bist dran" shows the human a conflict that an agent leaves (PR#43).
- No automatic rebase; the agent resolves a conflict itself.
- No GitHub.
## Specification
### Feature: the watch of a pull request ends when its agent must act
#### Scenario#48.01: a merge ends the watch
&hellip; so that the agent deploys at once.
- **Given** a watched pull request
- **When** Gitea merges it
- **Then** the watch ends with `MERGED` and the merge commit
##### Verified by
- [PullRequestWatchTest "the outcome of a pull request, in order of its states"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/PullRequestWatchTest.kt)
#### Scenario#48.02: a conflict with main ends the watch
&hellip; so that the agent rebases before the human has to notice.
- **Given** a watched pull request
- **When** another pull request merges and Gitea reports a conflict in two checks in a row
- **Then** the watch ends with `CONFLICT`
##### Verified by
- [PullRequestWatchTest "a conflict counts only when Gitea reports it twice"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/PullRequestWatchTest.kt)
#### Scenario#48.03: a red build ends the watch
&hellip; so that the agent repairs the build.
- **Given** a watched pull request
- **When** the build of its head, e.g. by Werkator, fails
- **Then** the watch ends with `FAILED`, the context and its description
##### Verified by
- [PullRequestWatchTest "the outcome of a pull request, in order of its states"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/PullRequestWatchTest.kt)
#### Scenario#48.04: a green build of a WIP pull request ends the watch
&hellip; so that the agent removes `WIP: ` and starts the review.
- **Given** a watched pull request with the title prefix `WIP: `
- **When** all statuses of its head are successful
- **Then** the watch ends with `READY`
##### Verified by
- [PullRequestWatchTest "the outcome of a pull request, in order of its states"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/PullRequestWatchTest.kt)
#### Scenario#48.05: a review that asks for work ends the watch
&hellip; so that the agent answers each review round.
- **Given** a watched pull request in review by werkrevisor
- **When** werkrevisor finishes with RED, with a verdict that needs a human, or with open hints
- **Then** the watch ends with `REVIEW` and werkrevisor's description
##### Verified by
- [PullRequestWatchTest "the outcome of a pull request, in order of its states"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/PullRequestWatchTest.kt)
#### Scenario#48.06: a running review does not end the watch
&hellip; so that the agent is not woken for nothing.
- **Given** a watched pull request whose build and review still run
- **When** the watch checks it
- **Then** the watch goes on
##### Verified by
- [PullRequestWatchTest "the outcome of a pull request, in order of its states"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/PullRequestWatchTest.kt)
#### Scenario#48.07: a draft behind main ends the watch
&hellip; so that a `WIP: ` pull request is rebased before its review, too.
- **Given** a watched pull request with the title prefix `WIP: `
- **When** another pull request merges into `main`
- **Then** the watch ends with `BEHIND`
##### Verified by
- [PullRequestWatchTest "the outcome of a pull request, in order of its states"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/PullRequestWatchTest.kt)
## Solution
- `werkregie-dashboard watch-pr <URL of the pull request>` checks the pull request every 30 s, `--interval` changes it.
It prints one line and ends, by the first that applies:
`MERGED <commit>`, `CLOSED`, `BEHIND`, `CONFLICT`, `FAILED <context>: <description>`, `REVIEW <description>`, `READY`.
- The token comes from the entry of `giteas` in `dashboard.yml` whose `url` matches the pull request,
as for the dashboard: `token`, `tokenEnv` or `teaLogin`; the watch only reads.
- Gitea computes `mergeable` after a push or merge in the background;
a single `false` can be stale, so a conflict counts after two checks in a row.
- Gitea reports a draft, i.e. a pull request with `WIP: `, as never mergeable.
For a draft, the watch compares the merge base with the head of `main` instead and ends with `BEHIND` when they differ.
Its first run on PR#48 ended with a false `CONFLICT` for this reason.
- The watch reads the review from werkrevisor's two statuses of the head, `ai-review/gate` and `ai-review/assessment`.
It ends with `REVIEW` when the review asks the agent for work:
- one of the two statuses is a failure or an error, e.g. after the verdict RED;
- or the review is done, and `ai-review/assessment` is not a success, e.g. pending after YELLOW or a warning after ORANGE;
- or `ai-review/assessment` is a success, but its description says that open hints hold the merge.
- Its first run in a review missed ORANGE, since it looked for werkrevisor's words of YELLOW only.
- A failure or an error of any other status, e.g. of Werkator's build, ends the watch with `FAILED`.
- After 10 failed requests in a row, the watch ends with an error and exit code 3.
- `doc/agentic-workflow.md`, the master copy:
- the agent watches its pull request after each push until it must act;
- a conflict with `main` is the agent's to fix at once;
- the version is the next free one over `main` and the open pull requests, and is raised again after a rebase.
- Version 0.29.0.
## Open Questions
- [x] Where does the watch live: in a script beside `git-push-as-claude-code.sh`, or in a mode of werkregie-dashboard?
In a mode of werkregie-dashboard, chosen by the human.
It is written in Kotlin and tested like the rest of werkregie, and it reads the Gitea tokens from `dashboard.yml`.
## Additional Changes
- An entry of `dashboard.yml` that this version does not know is named on stderr and skipped, no longer fatal.
On 2026-10-12, PR#47 added `claude.summary` to the configuration on think before its deployment;
`watch-pr` failed on it, and so would the relay of version 0.26.0 at its next start, until PR#47 merged.