nisse-review: later reviews, replies, rounds, stop and review (M7.5)

Back on a PR it reviewed: a new head gets a later review of the commits
since (each one's diff, through the API) and of its earlier points; what
was said since gets replies, answered where it was said or skipped with a
reason. It signs off once and stays quiet while still happy. Past
tuning.review.max_rounds it sums up for the operator and goes quiet,
until a push or someone other than the PR's author joins in. An
operator's `stop` quiets it; `review` asks for a fresh review.

Reviews carry numbered markers and replies their point's key, so
recovery posts nothing twice.

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-17 07:06:06 +00:00
1 parent 7e73b8fb37
commit 37bbac7e72
14 files changed
+1268 -157

No files matched your search

+27 -6
View File
@@ -9,15 +9,23 @@ from nisse_core.state import store
from nisse_core.state.store import WorkItem
# A review item's positions.
READY = "ready" # to review
RUNNING = "running" # a review run is under way; on boot, interrupted
REVIEWED = "reviewed" # reviewed at its head; later events are M7.5's
READY = "ready" # a run is due: a review, a later review, or replies
RUNNING = "running" # a run is under way; on boot, interrupted
REVIEWED = "reviewed" # nothing due: watching for pushes and comments
WAITING = "waiting" # not ready: a draft or work in progress
FAILED = "failed" # reviews kept failing; said so on the PR
FAILED = "failed" # runs kept failing; said so on the PR
CAPPED = "capped" # the round cap: summed up for the operator, quiet (R2)
STOPPED = "stopped" # an operator said `stop` on the PR; `review` resumes (R5)
CLOSED = "closed"
POST_REVIEW = "post_review" # the intent around posting one (L8)
# What a run is, from the item (``Scheduler._kind``).
FIRST = "first" # the whole PR: its first review, or one asked for
LATER = "later" # what changed since its last review, and its earlier points
REPLIES = "replies" # answering what was said since, at the same head
SUMMARY = "summary" # the round cap: what is still open, for the operator
class StateStore(store.StateStore):
AGENT = "nisse-review"
@@ -31,9 +39,17 @@ class PR:
repo: str # owner/name
number: int
head: str = "" # the commit last seen at the PR's head
reviewed: str = "" # the commit its review was posted on
reviewed: str = "" # the commit its last review was posted on
attempts: int = 0 # failed runs at this head
seen_at: float = 0.0 # when this head was first seen (the CI wait)
# M7.5: the discussion.
seen: tuple[str, ...] = () # keys of everything said on it, taken in
fresh: tuple[str, ...] = () # keys said since its last run, to answer
rounds: int = 0 # reviews and reply sets since the last reset (R2)
posts: int = 0 # reviews it posted on the PR: their markers' numbers
happy: bool = False # its last review signed off: no repeats while so
full: bool = False # an operator asked for a fresh review (`review`)
run: str = "" # the kind of run under way, for recovery
def item_id(repo: str, number: int) -> str:
@@ -41,7 +57,12 @@ def item_id(repo: str, number: int) -> str:
def pr_of(item: WorkItem) -> PR:
return PR(**item.data["pr"])
raw = dict(item.data["pr"])
for key in ("seen", "fresh"):
raw[key] = tuple(raw.get(key) or ())
if "posts" not in raw and raw.get("reviewed"):
raw["posts"] = 1 # reviewed before M7.5, which numbered them
return PR(**raw)
def data(pr: PR) -> dict[str, Any]: