Development Plan M7.3: a new nisse-review package over nisse-core. It reviews every ready PR in the repositories its Gitea account can read, and posts one COMMENT review on each.
How it works
Duty and items (R1).
Each round, it lists the open PRs in every readable repository, or only those in the optional repositories list.
Each PR gets a work item. A PR is ready unless it's a draft or its title marks it WIP.
The oldest ready item is reviewed first, one at a time. An item closes when its PR merges or closes.
PRs that were already open when it started get reviewed too, as you decided.
Reading (R3).
It downloads the repository at the PR's head and base as Gitea archives, unpacks them into a scratch folder, and reads them in Python with read_file, list_files and search_code.
read_issue and list_issues read the repository's issues.
Every read is bound to the item's repository. There's no git and no shell: the model chooses only paths and search text. Paths that escape the tree, links and devices in archives are all refused.
A review.
It starts from the PR, its commits and diff, what's been said on it, the issues it closes, and the CI status.
comment drafts a point on any line of any file, after checking the line exists at the head.
post_review posts one COMMENT review at the head; it never approves or requests changes. If Gitea refuses an inline comment off the diff, that comment goes into the summary as a link to the line.
It has ask_expert, and the vault and memory under memory/nisse-review, with notes per repository.
Durability (L8). Posting is recorded as an intent. After a crash, it asks Gitea whether the review (marked with its head commit) was posted, so nothing is posted twice. If Gitea is down, the item waits.
Failures. A failed run is retried. After tuning.review.attempts failures, it says so on the PR and waits for the next push.
Access. Startup refuses an account that can push to, or administer, any repository it reads.
Changes to nisse-core/gitea.py:
new methods: readable_repositories, open_pull_requests, pull_request_diff, pull_request_commits, archive, issue, search_issues, reviews and post_review (COMMENT only);
PullRequest gains title, body, author, base and draft.
Bring-up and CI
scripts/review-bringup.sh runs it as a transient systemd unit from the exported code. Its config and secrets live in /root/nisse-review, and there's a new nisse-review.example.yaml.
The Dockerfile copies the new workspace member, and the image still builds.
CI passes NISSE_REVIEW_GITEA_TOKEN to the e2e jobs.
Tests
21 unit tests run against a fake Gitea and a scripted model. They cover selecting ready PRs, a full review, comments off the diff turning into links, line checks, the reader's bounds and archive unpacking, memory, retries, recovery after a crash, the access check and config.
New Gitea client tests.
A live test, test_review_live.py, marked e2e but not smoke: a fresh test repository and a PR, reviewed by the local model. It passed locally in 64 seconds. It checks that the account is refused while it has write access, then that exactly one COMMENT review is posted at the head.
Locally, the unit tier passes (858 tests).
Docs: the Development Plan marks M7.3 done, and the Tuning Guide documents tuning.review.*.
Needed before merging: the repository's Actions secret NISSE_REVIEW_GITEA_TOKEN, holding the review account's token. Without it, the live test fails on main's full run.
Development Plan M7.3: a new `nisse-review` package over `nisse-core`. It reviews every ready PR in the repositories its Gitea account can read, and posts one COMMENT review on each.
**How it works**
- **Duty and items (R1).**
- Each round, it lists the open PRs in every readable repository, or only those in the optional `repositories` list.
- Each PR gets a work item. A PR is ready unless it's a draft or its title marks it WIP.
- The oldest ready item is reviewed first, one at a time. An item closes when its PR merges or closes.
- PRs that were already open when it started get reviewed too, as you decided.
- **Reading (R3).**
- It downloads the repository at the PR's head and base as Gitea archives, unpacks them into a scratch folder, and reads them in Python with `read_file`, `list_files` and `search_code`.
- `read_issue` and `list_issues` read the repository's issues.
- Every read is bound to the item's repository. There's no git and no shell: the model chooses only paths and search text. Paths that escape the tree, links and devices in archives are all refused.
- **A review.**
- It starts from the PR, its commits and diff, what's been said on it, the issues it closes, and the CI status.
- `comment` drafts a point on any line of any file, after checking the line exists at the head.
- `post_review` posts one COMMENT review at the head; it never approves or requests changes. If Gitea refuses an inline comment off the diff, that comment goes into the summary as a link to the line.
- It has `ask_expert`, and the vault and memory under `memory/nisse-review`, with notes per repository.
- **Durability (L8).** Posting is recorded as an intent. After a crash, it asks Gitea whether the review (marked with its head commit) was posted, so nothing is posted twice. If Gitea is down, the item waits.
- **Failures.** A failed run is retried. After `tuning.review.attempts` failures, it says so on the PR and waits for the next push.
- **Access.** Startup refuses an account that can push to, or administer, any repository it reads.
**Changes to `nisse-core/gitea.py`:**
- new methods: `readable_repositories`, `open_pull_requests`, `pull_request_diff`, `pull_request_commits`, `archive`, `issue`, `search_issues`, `reviews` and `post_review` (COMMENT only);
- `PullRequest` gains `title`, `body`, `author`, `base` and `draft`.
**Bring-up and CI**
- `scripts/review-bringup.sh` runs it as a transient systemd unit from the exported code. Its config and secrets live in `/root/nisse-review`, and there's a new `nisse-review.example.yaml`.
- The Dockerfile copies the new workspace member, and the image still builds.
- CI passes `NISSE_REVIEW_GITEA_TOKEN` to the e2e jobs.
**Tests**
- 21 unit tests run against a fake Gitea and a scripted model. They cover selecting ready PRs, a full review, comments off the diff turning into links, line checks, the reader's bounds and archive unpacking, memory, retries, recovery after a crash, the access check and config.
- New Gitea client tests.
- A live test, `test_review_live.py`, marked `e2e` but not smoke: a fresh test repository and a PR, reviewed by the local model. It passed locally in 64 seconds. It checks that the account is refused while it has write access, then that exactly one COMMENT review is posted at the head.
- Locally, the unit tier passes (858 tests).
**Docs:** the Development Plan marks M7.3 done, and the Tuning Guide documents `tuning.review.*`.
**Needed before merging:** the repository's Actions secret `NISSE_REVIEW_GITEA_TOKEN`, holding the review account's token. Without it, the live test fails on main's full run.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
A new package over nisse-core: it reviews every ready PR in the
repositories its account can read, and posts one COMMENT review.
- scheduler: each round, open PRs per readable repository (or the
configured repositories); an item per PR, ready unless draft or WIP;
closed with the PR. One review at a time, oldest first.
- reader: the repository at head and base from Gitea archives, unpacked
and read in Python (read_file, list_files, search_code), and its issues;
bound to the item's repository; no git, no shell.
- review: starts from the PR, commits, diff, comments, closed issues, CI;
comment drafts a point on any existing line; post_review posts one
COMMENT review at the head, inline where Gitea takes it, else as links in
the summary. ask_expert; vault and memory (memory/nisse-review).
- L8: posting is an intent; recovery asks Gitea for the marked review and
never posts twice; with Gitea down, the item waits.
- Failures retried, then said on the PR until the next push.
- Startup refuses an account that can push to or administer any repo.
- nisse-core gitea: readable_repositories, open_pull_requests, diff,
commits, archive, issue, search_issues, reviews, post_review; PR fields.
- Bring-up: scripts/review-bringup.sh; nisse-review.example.yaml.
- Tests: unit over a fake Gitea and scripted model; a live test
(NISSE_REVIEW_GITEA_TOKEN) reviews a PR in a fresh test repository.
- Docs: Development Plan M7.3, Tuning Guide (tuning.review.*).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Development Plan M7.3: a new
nisse-reviewpackage overnisse-core. It reviews every ready PR in the repositories its Gitea account can read, and posts one COMMENT review on each.How it works
repositorieslist.read_file,list_filesandsearch_code.read_issueandlist_issuesread the repository's issues.commentdrafts a point on any line of any file, after checking the line exists at the head.post_reviewposts one COMMENT review at the head; it never approves or requests changes. If Gitea refuses an inline comment off the diff, that comment goes into the summary as a link to the line.ask_expert, and the vault and memory undermemory/nisse-review, with notes per repository.tuning.review.attemptsfailures, it says so on the PR and waits for the next push.Changes to
nisse-core/gitea.py:readable_repositories,open_pull_requests,pull_request_diff,pull_request_commits,archive,issue,search_issues,reviewsandpost_review(COMMENT only);PullRequestgainstitle,body,author,baseanddraft.Bring-up and CI
scripts/review-bringup.shruns it as a transient systemd unit from the exported code. Its config and secrets live in/root/nisse-review, and there's a newnisse-review.example.yaml.NISSE_REVIEW_GITEA_TOKENto the e2e jobs.Tests
test_review_live.py, markede2ebut not smoke: a fresh test repository and a PR, reviewed by the local model. It passed locally in 64 seconds. It checks that the account is refused while it has write access, then that exactly one COMMENT review is posted at the head.Docs: the Development Plan marks M7.3 done, and the Tuning Guide documents
tuning.review.*.Needed before merging: the repository's Actions secret
NISSE_REVIEW_GITEA_TOKEN, holding the review account's token. Without it, the live test fails on main's full run.🤖 Generated with Claude Code
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk