M7.3: nisse-review's first slice #149

Merged
dongar merged 1 commits from nisse-review into main 2026-10-16 19:19:43 +00:00
Collaborator

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.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk

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
dev-agent added 1 commit 2026-10-13 22:34:17 +00:00
M7.3: nisse-review's first slice
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m49s
CI / e2e (pull_request) Successful in 3m18s
f59d974a95
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
dongar approved these changes 2026-10-16 19:08:16 +00:00
dongar merged commit 5126f0717d into main 2026-10-16 19:19:43 +00:00
dongar deleted branch nisse-review 2026-10-16 19:19:43 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dev/nisse#149