nisse-review: read code from a git clone kept between runs
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m3s
CI / e2e (pull_request) Successful in 4m5s

Archives were downloaded and unpacked afresh for every run, which doesn't
scale to very large repositories. Each repository is now cloned once, with
its whole history, under scratch/repos/<owner>/<name>/ (head/ the clone,
base/ a worktree when read). A run fetches every branch and the PR's
refs/pull/N/head, checks out the head exactly, and fails if that head is
gone. Only the latest repository is kept for now.

Git runs walled off: none of the host's git configuration, links checked
out as plain files, no hooks, submodules or LFS, only the Gitea URL's
protocol, and the token as an http.extraHeader for that URL in git's
environment only. A stop ends a git command; a cut-short first clone is
removed. The reader never shows .git. GiteaClient.archive is gone.

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 20:37:12 +00:00
1 parent a85e855fc2
commit 3813ad55d6
11 files changed
+642 -139

No files matched your search

+5 -1
View File
@@ -928,7 +928,11 @@ Two agents can argue forever, so a PR's exchanges are capped.
administer, a repository it reviews is refused, and stops if granted that
while running. 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.
comment is already visible to that repository's readers. The code is a git
clone kept on its host between runs, checked out at the PR's head (and its
base when asked); git runs only the harness's fixed commands, walled off from
the host's git configuration and from anything the code could make it run
(links, hooks, submodules).
- **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.
+17 -2
View File
@@ -616,7 +616,9 @@ waits until two exist to compare. Moved, unchanged in behaviour:
`list_files`, `search_code`; a long file is read in parts, `start` giving
the line, since a file cut at 20,000 characters left its end out of reach),
and its issues (`read_issue`,
`list_issues`): no git, no shell, bound to the item's repository.
`list_issues`): no shell, bound to the item's repository. Since the M7.5
follow-ups, the code comes from a git clone kept between runs, not an
archive per run (see there).
- **A review:** starts from the PR, its commits, diff, comments, the issues it
closes and CI; `comment` drafts a point on any line of any file (checked to
exist), `post_review` posts one COMMENT review at the head. Gitea takes
@@ -699,9 +701,22 @@ of earlier points, replies on threads, the round cap and its reset, `stop` and
PR goes back to ready with no failure counted; a repository Gitea won't
list is logged when that starts or changes (`intake.repo_failed`), and when
it ends (`intake.repo_ok`), not every round. Tests: `test_review_stop.py`.
- **Code from a kept clone** (operator: archives don't scale to very large
repositories): each repository is cloned once, with its whole history, under
`scratch/repos/<owner>/<name>/` (`head/` the clone, `base/` a worktree made
when read). A run fetches every branch and the PR's `refs/pull/N/head`,
checks out the head exactly (`checkout --force`, `clean -ffdx`), and fails if
that head is gone. Only the latest repository is kept for now; the layout
holds several. Git runs walled off: none of the host's git configuration,
links as plain files, no hooks, submodules or LFS, only the Gitea URL's
protocol, and the token as an `http.extraHeader` for that URL in git's
environment only. A stop ends a git command, and a cut-short first clone is
removed. `review.clone` / `review.fetch` log the time and size. No size
limits yet. Tests: `test_clones.py`, against local bare repositories.
**M7.6 Deployment.** *Waits for the agent host (Phase 6).* Its image, container,
Gitea account and secrets (R5).
Gitea account and secrets (R5). The image needs `git`, and `scratch/` a
persistent volume, so clones outlive a restart.
### Phase 8 — `nisse-ops`