Spec: nisse-review (Part R) before nisse-ops
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m42s
CI / e2e (pull_request) Successful in 3m59s

A review agent comes second: it reads every ready PR in the repositories
it can see and discusses its points on the PR, never changing code.

- Architecture Part R: duty and items (R1), rounds capped and reset by
  anyone else commenting (R2), read-only reach bound to the item's
  repository, no approvals, no build VM for now (R3), peers (R4), local
  model with ask_expert, no Matrix room for now, own identity (R5).
- D5 and L11: a peer's review is constructive criticism, answered point by
  point; never an instruction, approval or go-ahead; nisse-dev doesn't
  wait for it before asking to merge.
- Part Q: letting nisse-review approve a limited subset of PRs.
- Development Plan: Phase 7 is nisse-core and nisse-review (M7.2-M7.6);
  nisse-ops moves to Phase 8 (M8.1-M8.5), with the MCP decisions so far.

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 19:39:15 +00:00
1 parent 873f1f5bf2
commit 175ed0771d
3 files changed
+138 -14

No files matched your search

+96
View File
@@ -19,6 +19,7 @@ overnight and expects to be treated well in return.
|---|---|---| |---|---|---|
| Shared library | `nisse-core` | What every agent is built on (Part L) | | Shared library | `nisse-core` | What every agent is built on (Part L) |
| Dev Agent | `nisse-dev` | Designs and builds software projects (Part D) | | Dev Agent | `nisse-dev` | Designs and builds software projects (Part D) |
| Review Agent | `nisse-review` | Reviews pull requests (Part R) |
| Sysadmin Agent | `nisse-ops` | Operates infrastructure (Part O) | | Sysadmin Agent | `nisse-ops` | Operates infrastructure (Part O) |
More agents are planned (3D design work, video-file management), each another More agents are planned (3D design work, video-file management), each another
@@ -519,6 +520,8 @@ can do rather than trying to prevent steering.
- **Instruction channels are operator-scoped:** Matrix from allowlisted identities, - **Instruction channels are operator-scoped:** Matrix from allowlisted identities,
comments and assignments from allowlisted accounts. When someone else wrote an comments and assignments from allowlisted accounts. When someone else wrote an
issue, the operator's assignment is the instruction and the text stays data. issue, the operator's assignment is the instruction and the text stays data.
Another agent's comments are a peer's (R4): weighed on their merits, labelled
as such, never instructions.
- **Intake is parsed into structured fields in code** where possible, and what - **Intake is parsed into structured fields in code** where possible, and what
reaches a model is labelled by source (L6). reaches a model is labelled by source (L6).
- **Approval messages are rendered by code;** the classifier never sees untrusted - **Approval messages are rendered by code;** the classifier never sees untrusted
@@ -776,6 +779,12 @@ recovers a VM for now; `nisse-ops` later (O5).
- **Operator comments on a PR** (conversation and inline) are instructions; the - **Operator comments on a PR** (conversation and inline) are instructions; the
agent answers with commits to the same PR, replying where the comment was made. agent answers with commits to the same PR, replying where the comment was made.
Intake is as in L8. Intake is as in L8.
- **A peer's review** (`nisse-review`, Part R) is constructive criticism, not
instruction: the agent answers every point, changing the code within the item's
scope or pushing back with its reasons, and asks the operator about anything that
would change the scope. A peer's comment never approves, never counts as a
go-ahead and never runs a command. The agent doesn't wait for a review before
asking the operator to merge: the operator may weigh in on the discussion.
- **The PR may have moved on meanwhile.** After pushing a fix, the agent checks the - **The PR may have moved on meanwhile.** After pushing a fix, the agent checks the
PR: if it merged before the fix landed, the fix goes out as a new PR; if it was PR: if it merged before the fix landed, the fix goes out as a new PR; if it was
closed, the agent says so and asks. closed, the agent says so and asks.
@@ -846,6 +855,91 @@ secret's visibility itself.
--- ---
## Part R — `nisse-review`
Reviews every pull request that is ready, in the repositories it can see, and
discusses its points on the PR. It reads and comments; it never changes code. Its
purpose is a second, always-available look that pushes `nisse-dev` (and anyone
else) towards better changes.
### R1. How it works
- **The duty:** watching the repositories its Gitea account can read for PRs
ready for review: open, not a draft, and not marked work in progress. Any
author's: the operator's, `nisse-dev`'s, or another tool's.
- **A work item per PR,** scoped to its repository (L1). It closes when the PR
merges or closes.
- **What starts a run:** the PR becoming ready, a new push, or a new comment or
review on it.
- **The first review** is given the PR (title, description, its commits and diff,
its comments, and the issue it closes); the reviewer reads the rest as it needs
it (R3).
- **A later review** covers what changed since its last one **and** its earlier
points: whether each was addressed, answered, or left alone, and it says so when
one was left unaddressed.
- **What it checks:** code health; consistency with the PR's description, its
issue, and the repository's documented architecture and conventions; tests for
what changed, judged by reading them; and the CI result as Gitea reports it.
- **It discusses on the PR,** in inline threads and top-level comments, and
answers replies to its points: it concedes a good argument and holds a point it
still believes, with reasons.
### R2. Rounds
Two agents can argue forever, so a PR's exchanges are capped.
- **A round** is one review or set of replies the reviewer posts. After a capped
number of rounds (a tuning value) it posts a summary of what is still open, for
the operator, and stops answering on that PR.
- **Anyone else commenting resets the count:** someone other than the reviewer
and the PR's agent author (in practice, the operator) has joined the
discussion. A push doesn't.
### R3. Authority and reach
- **Read-only on the repository:** code at any ref (not only what the PR touches),
documents, issues, other PRs, CI status, through its Gitea account. Reads are
bound by the harness to the item's repository, so what it might repeat in a
comment is already visible to that repository's readers.
- **It writes only comments and reviews on the item's PR.** Its reviews never
approve and never request changes: the operator's approval alone counts for
merging (D5), and Gitea's branch protection enforces it.
- **No execution for now:** no build VM and no commands (L13); coverage is judged
by reading the tests until a CI coverage report exists. If a reason appears
(running tests itself, measuring coverage), it can have a build VM of its own,
like `nisse-dev`'s (D3, D7).
- **Its own memory** (L6.4), with notes per repository (L6.3), so it learns each
project's conventions and what the operator accepted or overruled.
| Class | Examples |
|---|---|
| Harmless | Reading the repository, its issues and PRs, CI status, its notes |
| Contained (in scope) | Reviews, comments and replies on its item's PR; its own notes |
| Dangerous | Nothing is offered: no approval, no merge, no other repository |
### R4. Its peers
- **Its comments are a peer's** (L1, L11): `nisse-dev` weighs them and answers
(D5). Neither side's comments are instructions to the other.
- **The operator's comments on a PR** are the operator's: the reviewer may take
them as settling a point, and stops pressing it.
- **Prompt injection** (L11): PR text and code are untrusted; a steered reviewer can
only post a misleading comment on that PR, which the operator reads with the rest.
### R5. Models and deployment
- **The same local model drives it** as `nisse-dev` for now (P7), with
`ask_expert` for a frontier model's view (L9).
- **No Matrix room** for now: the PR is its conversation. If every agent gets a
room, it gets one too. Until then the operator stops it on a PR by commenting
`stop` there (a review is asked for again with `review`), and stops it entirely
by stopping its container.
- **Its own identity** (L12): a Gitea account (`nisse-review`) with read access to
the repositories it reviews, its own token and LiteLLM key, and its own
container on the agent host (X1), reaching only Gitea and LiteLLM.
---
## Part O — `nisse-ops` ## Part O — `nisse-ops`
Operates infrastructure across both sites, driven by issues and duties, with Operates infrastructure across both sites, driven by issues and duties, with
@@ -1030,6 +1124,8 @@ shared between agents.
sometimes from release branches: what the agent may do (a release is visible sometimes from release branches: what the agent may do (a release is visible
beyond its own area, so dangerous unless the trust dial says otherwise) and how a beyond its own area, so dangerous unless the trust dial says otherwise) and how a
build plan names one. build plan names one.
- **Letting `nisse-review` approve a limited subset of PRs:** which subset, and
how Gitea would enforce it (R3).
- **Recovering the agent host.** It is a single point of failure for every agent, - **Recovering the agent host.** It is a single point of failure for every agent,
`nisse-ops` included, so its recovery can't depend on ops running. A recovery `nisse-ops` included, so its recovery can't depend on ops running. A recovery
plan (rebuilding the host, restoring state from snapshots) is ops work still to plan (rebuilding the host, restoring state from snapshots) is ops work still to
+41 -13
View File
@@ -435,7 +435,7 @@ past the usual fixes (D3).
- `loop.qnt` gained the block (`blocked_vm_runs_nothing`); it found that work in - `loop.qnt` gained the block (`blocked_vm_runs_nothing`); it found that work in
progress was lost on a reimage, hence parking first. The walk requests progress was lost on a reimage, hence parking first. The walk requests
reimages and recovers them, reimaged or not. reimages and recovers them, reimaged or not.
- Later, nisse-ops reimages a VM itself (M6.2, M7.6); a changed address means - Later, nisse-ops reimages a VM itself (M6.2, M8.5); a changed address means
editing `build_host` and restarting. editing `build_host` and restarting.
**M4.4** *(#137, with the operator).* **Done** (this PR). A VM that can't be **M4.4** *(#137, with the operator).* **Done** (this PR). A VM that can't be
@@ -525,7 +525,7 @@ notes are written and read (L6.4).
corrected; don't remember what it already holds. corrected; don't remember what it already holds.
- Decided with the operator: `ARCHITECTURE.md` isn't put into every item's - Decided with the operator: `ARCHITECTURE.md` isn't put into every item's
context (its size); the driver still reads it as told. The infrastructure context (its size); the driver still reads it as told. The infrastructure
world model waits for `nisse-ops` (Phase 7). world model waits for `nisse-ops` (Phase 8).
- The walk writes project notes and checks they come only from that project's - The walk writes project notes and checks they come only from that project's
items. items.
@@ -553,7 +553,11 @@ LLDAP principal (L12).
**M6.5 The browser tool.** *Pre-approved.* On the internal test network (D7). **M6.5 The browser tool.** *Pre-approved.* On the internal test network (D7).
### Phase 7 — `nisse-core` and `nisse-ops` ### Phase 7 — `nisse-core` and `nisse-review`
`nisse-review` (Part R) comes before `nisse-ops`: it writes only comments, so it
needs none of ops' machinery or infrastructure, while being a real second user of
the loop. Decided with the operator, 2026-10-13.
**M7.1 Extract `nisse-core`.** *Done.* What isn't the dev agent's moved to **M7.1 Extract `nisse-core`.** *Done.* What isn't the dev agent's moved to
`nisse-core`, unchanged in behaviour, with its tests: the transcript and its `nisse-core`, unchanged in behaviour, with its tests: the transcript and its
@@ -569,16 +573,39 @@ says so).
`nisse_dev.state.StateStore` adds the build VM blocks (D3); stores already at `nisse_dev.state.StateStore` adds the build VM blocks (D3); stores already at
version 5 open unchanged. version 5 open unchanged.
- The vault names whose writes it records (`agent`), rather than assuming one. - The vault names whose writes it records (`agent`), rather than assuming one.
- Deferred until `nisse-ops` has a loop to share: splitting the loop (`loop/*`) - Deferred until a second agent has a loop to share: splitting the loop
and configuration into a common part and the dev agent's. The dev agent's (`loop/*`) and configuration into a common part and the dev agent's. The dev
`git`, delegation, `ask_expert`, executor tools and evaluation capture stay agent's `git`, delegation, `ask_expert`, executor tools and evaluation capture
in `nisse-dev`. stay in `nisse-dev`.
**M7.2 MCP servers** (#37). **M7.3 Approved step **M7.2 Share the loop.** *Needs discussion.* What of `nisse_dev.loop` and
sequences** (O2). **M7.4 Ops intake and duties** from the infrastructure `config` is any agent's moves to `nisse-core`, shaped by what `nisse-review`
repository (O5). **M7.5 The ops tool VM and deployment** (O6). **M7.6 Requests needs; `nisse-dev` runs on it unchanged.
between agents:** `nisse-dev` asking for build VMs (L1, O5). All *needs discussion*
except M7.1. **M7.3 `nisse-review`'s first slice.** *Needs discussion.* The duty and a work item
per ready PR (R1), read-only tools bound to the item's repository (R3), a first
review posted on the PR; bring-up on this box, and an end-to-end test against a
PR in a test repository.
**M7.4 `nisse-dev` answers a peer.** *Pre-approved.* Peer comments labelled as a
peer's, answered point by point with a change or a reason (D5, R4).
**M7.5 Discussion and rounds.** *Pre-approved.* Later reviews of what changed and
of earlier points, replies on threads, the round cap and its reset, `stop` and
`review` on a PR (R1, R2, R5).
**M7.6 Deployment.** *Waits for the agent host (Phase 6).* Its image, container,
Gitea account and secrets (R5).
### Phase 8 — `nisse-ops`
**M8.1 MCP servers** (#37). Settled so far: redaction scrubs each instance's own
credentials (L12), with masking what an API returns as #144; search-then-dispatch
meta-tools are avoided, not ruled out (X3). **M8.2 Approved step sequences** (O2).
**M8.3 Ops intake and duties** from the infrastructure repository (O5), taking over
the Terraform the operator is writing now. **M8.4 The ops tool VM and deployment**
(O6). **M8.5 Requests between agents:** `nisse-dev` asking for build VMs (L1, O5).
All *needs discussion*.
### Not yet scheduled ### Not yet scheduled
@@ -598,7 +625,7 @@ vision; one local model for every agent; several forges (#26); a room per projec
| #27 per-project state, clone on use | M1.3 (clone on the build VM), M1.4 (projects) | | #27 per-project state, clone on use | M1.3 (clone on the build VM), M1.4 (projects) |
| #28 room per project | Not yet scheduled (threads, M2.3, may make it unnecessary) | | #28 room per project | Not yet scheduled (threads, M2.3, may make it unnecessary) |
| #29 create a project from scratch | M2.5 (ownership settled by D4, D8) | | #29 create a project from scratch | M2.5 (ownership settled by D4, D8) |
| #37 MCP servers | M7.2 | | #37 MCP servers | M8.1 |
| #42 per-turn commentary | M2.7 | | #42 per-turn commentary | M2.7 |
| #62 branch at execution start | M2.1 | | #62 branch at execution start | M2.1 |
| #65 agent merges approved PRs | M3.3 | | #65 agent merges approved PRs | M3.3 |
@@ -606,6 +633,7 @@ vision; one local model for every agent; several forges (#26); a room per projec
| #70 watch stall | M2.1 | | #70 watch stall | M2.1 |
| #77 conversational agent | M2.3 (questions), M2.7 | | #77 conversational agent | M2.3 (questions), M2.7 |
| #137 block on an unreachable build VM | M4.4 | | #137 block on an unreachable build VM | M4.4 |
| #144 redact secrets an MCP server returns | After M8.1 |
## 6. Implementation notes ## 6. Implementation notes
+1 -1
View File
@@ -19,4 +19,4 @@ never imports an agent.
(`nisse-dev`'s are in `nisse_dev.loop.actions`). (`nisse-dev`'s are in `nisse_dev.loop.actions`).
The loop is still `nisse-dev`'s, until `nisse-ops` has one to share it with The loop is still `nisse-dev`'s, until `nisse-ops` has one to share it with
(Development Plan M7.1). (Development Plan M7.2).