Stacked on #159 (it uses #159's stop flag and Stopped); I'll retarget it to main once #159 merges.
Why: archives were downloaded and unpacked afresh for every run that read code; that doesn't scale to very large repositories.
What
nisse_review.clones: each repository is cloned once, full history, under scratch/repos/<owner>/<name>/. head/ is the clone, checked out at the PR's head. base/ is a git worktree, made only when the model reads ref: base.
Each run fetches every branch plus refs/pull/N/head (which also covers PRs from forks), then checkout --force --detach <head> and clean -ffdx, so nothing from an earlier PR is left. If the planned head is gone (force-pushed since), the run fails and the next round picks up the new head.
Only the most recently used repository is kept (keep=1); the layout holds several.
reader.Repository reads plain folders as before, through a small Trees interface; .git is never listed, searched or read. GiteaClient.archive and the tar unpacking are removed.
review.clone / review.fetch log seconds and size.
Git walled off from the code it fetches
None of the host's git configuration (GIT_CONFIG_GLOBAL=/dev/null, GIT_CONFIG_NOSYSTEM, own HOME, GIT_CEILING_DIRECTORIES).
core.symlinks=false (links arrive as plain files), core.hooksPath=/dev/null, no submodules, GIT_LFS_SKIP_SMUDGE, GIT_ALLOW_PROTOCOL = the configured Gitea URL's scheme only.
The token is an http.<gitea url>/.extraHeader in git's environment (GIT_CONFIG_*): never on a command line, in .git/config, or in a URL.
A stop ends a running git command (SIGTERM, so git cleans its locks) and raises Stopped; a first clone cut short is deleted.
Tests:test_clones.py against local bare repositories: first clone, a later fetch leaving nothing of the last PR, a second repository replacing the first, the base worktree following its branch, a link arriving as a plain file, a vanished head failing, the token never in arguments or on disk, a stop ending a hanging fetch, and .git hidden from the reader. Each safety rule was mutation-checked (removing it fails a test). Plus a scheduler test: code git can't give fails the run.
Checked by hand against Gitea over HTTPS: clone of dev/nisse in 0.3 s (1.3 MB), the next run a 0.1 s fetch, no file containing the token.
Docs: Architecture R3, Development Plan (M7.5 follow-ups, M7.6 needs git and a persistent scratch/), nisse-review README.
Left out: size limits and a cache cap, keeping several repositories, diffs from local git, running tests in the checkout.
Stacked on #159 (it uses #159's stop flag and `Stopped`); I'll retarget it to `main` once #159 merges.
**Why:** archives were downloaded and unpacked afresh for every run that read code; that doesn't scale to very large repositories.
**What**
- `nisse_review.clones`: each repository is cloned once, full history, under `scratch/repos/<owner>/<name>/`. `head/` is the clone, checked out at the PR's head. `base/` is a git worktree, made only when the model reads `ref: base`.
- Each run fetches every branch plus `refs/pull/N/head` (which also covers PRs from forks), then `checkout --force --detach <head>` and `clean -ffdx`, so nothing from an earlier PR is left. If the planned head is gone (force-pushed since), the run fails and the next round picks up the new head.
- Only the most recently used repository is kept (`keep=1`); the layout holds several.
- `reader.Repository` reads plain folders as before, through a small `Trees` interface; `.git` is never listed, searched or read. `GiteaClient.archive` and the tar unpacking are removed.
- `review.clone` / `review.fetch` log seconds and size.
**Git walled off from the code it fetches**
- None of the host's git configuration (`GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM`, own `HOME`, `GIT_CEILING_DIRECTORIES`).
- `core.symlinks=false` (links arrive as plain files), `core.hooksPath=/dev/null`, no submodules, `GIT_LFS_SKIP_SMUDGE`, `GIT_ALLOW_PROTOCOL` = the configured Gitea URL's scheme only.
- The token is an `http.<gitea url>/.extraHeader` in git's environment (`GIT_CONFIG_*`): never on a command line, in `.git/config`, or in a URL.
- A stop ends a running git command (SIGTERM, so git cleans its locks) and raises `Stopped`; a first clone cut short is deleted.
**Tests:** `test_clones.py` against local bare repositories: first clone, a later fetch leaving nothing of the last PR, a second repository replacing the first, the base worktree following its branch, a link arriving as a plain file, a vanished head failing, the token never in arguments or on disk, a stop ending a hanging fetch, and `.git` hidden from the reader. Each safety rule was mutation-checked (removing it fails a test). Plus a scheduler test: code git can't give fails the run.
Checked by hand against Gitea over HTTPS: clone of `dev/nisse` in 0.3 s (1.3 MB), the next run a 0.1 s fetch, no file containing the token.
**Docs:** Architecture R3, Development Plan (M7.5 follow-ups, M7.6 needs `git` and a persistent `scratch/`), nisse-review README.
Left out: size limits and a cache cap, keeping several repositories, diffs from local git, running tests in the checkout.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Stacked on #159 (it uses #159's stop flag and
Stopped); I'll retarget it tomainonce #159 merges.Why: archives were downloaded and unpacked afresh for every run that read code; that doesn't scale to very large repositories.
What
nisse_review.clones: each repository is cloned once, full history, underscratch/repos/<owner>/<name>/.head/is the clone, checked out at the PR's head.base/is a git worktree, made only when the model readsref: base.refs/pull/N/head(which also covers PRs from forks), thencheckout --force --detach <head>andclean -ffdx, so nothing from an earlier PR is left. If the planned head is gone (force-pushed since), the run fails and the next round picks up the new head.keep=1); the layout holds several.reader.Repositoryreads plain folders as before, through a smallTreesinterface;.gitis never listed, searched or read.GiteaClient.archiveand the tar unpacking are removed.review.clone/review.fetchlog seconds and size.Git walled off from the code it fetches
GIT_CONFIG_GLOBAL=/dev/null,GIT_CONFIG_NOSYSTEM, ownHOME,GIT_CEILING_DIRECTORIES).core.symlinks=false(links arrive as plain files),core.hooksPath=/dev/null, no submodules,GIT_LFS_SKIP_SMUDGE,GIT_ALLOW_PROTOCOL= the configured Gitea URL's scheme only.http.<gitea url>/.extraHeaderin git's environment (GIT_CONFIG_*): never on a command line, in.git/config, or in a URL.Stopped; a first clone cut short is deleted.Tests:
test_clones.pyagainst local bare repositories: first clone, a later fetch leaving nothing of the last PR, a second repository replacing the first, the base worktree following its branch, a link arriving as a plain file, a vanished head failing, the token never in arguments or on disk, a stop ending a hanging fetch, and.githidden from the reader. Each safety rule was mutation-checked (removing it fails a test). Plus a scheduler test: code git can't give fails the run.Checked by hand against Gitea over HTTPS: clone of
dev/nissein 0.3 s (1.3 MB), the next run a 0.1 s fetch, no file containing the token.Docs: Architecture R3, Development Plan (M7.5 follow-ups, M7.6 needs
gitand a persistentscratch/), nisse-review README.Left out: size limits and a cache cap, keeping several repositories, diffs from local git, running tests in the checkout.
🤖 Generated with Claude Code
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk