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

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
This commit is contained in:
dongarandClaude Opus 5.5 committed 2026-10-13 22:34:02 +00:00
1 parent fc71d3da75
commit f59d974a95
24 files changed
+2330 -7

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
"""Entry point for the review agent: loads its configuration, checks its
access, and reviews until told to stop."""
import os
import signal
import sys
from pathlib import Path
from types import FrameType
from nisse_core.config import ConfigError
from nisse_core.envfile import load_env_file
from nisse_review.build import StartupError, build
from nisse_review.config import ReviewConfig
def main() -> int:
load_env_file(Path(os.environ.get("NISSE_ENV", ".env")).expanduser())
try:
config = ReviewConfig.load()
built = build(config)
except (ConfigError, StartupError) as exc:
print(f"nisse-review: {exc}", file=sys.stderr)
return 2
def shutdown(_signum: int, _frame: FrameType | None) -> None:
print("nisse-review: stopping", file=sys.stderr)
built.scheduler.stop()
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
print(
f"nisse-review: ready as {built.account}, reading "
+ (", ".join(built.repositories) or "no repositories"),
file=sys.stderr,
)
try:
built.scheduler.run_forever()
finally:
built.close()
return 0
if __name__ == "__main__":
sys.exit(main())