Beside the push gates, crafter-review runs the pre-send review of a kernel contributor's workbench: --mode series over a format-patch export with its cover letter, --mode reply over a mail draft. The inventory partitions a cover letter, a plain mail and the camera review's shape; ten kernel lenses join the table with their gates (structure, code, artifact truth) and the persona gate - one maintainer-persona instance per --reviewers entry, its whole pass gating its blocks and pruning the units that are not theirs. A series' code units are keyed on the code like the push gates'; every text unit on its text, the references it cites and the anchor when it is dated. The references an artifact cites are prefetched through utilities/lore.py into the gate-state, with a digest in the run layer, and a persona's calibration map is its lore listing. The kernel schema carries the persona vocabulary, the kernel note kinds, the predicted review and the not-mine list; the matrix the persona holes, the thin and the modelled units; gates clean without a persona pass is GATES CLEAN. A reply's gate-state sits beside the draft and is read once; a kernel mode needs --subsystem, defaults the effort to max and the trees to work/linux,work/milos-linux, and excuses no unit from the holes. The kernel lenses and the common text ship verbatim; the table lists them after the push gates' lenses and the config template holds them off, so a repository that never turns them on changes nothing. Prompts, keys and plans were checked byte for byte against the pipeline these modes replace, on its own artifacts: two replies and a series, every system prompt, user prompt, job and reuse key identical.
136 lines
12 KiB
Markdown
136 lines
12 KiB
Markdown
# Crafter Review
|
|
|
|
The pre-push review pipeline: adversarial lenses review a frozen commit range, one `claude --print` process per inventory unit, before the range is pushed. Five lenses in a fixed order: the approach, the range exercised in the running application through the Playwright MCP server, side by side the static bug proof and the security review, and last the commit truth of the messages and the documentation. Every lens is off until the repository's config turns it on, and the config says what each gets: the browser, the local Supabase database, both, or neither.
|
|
|
|
| Gate | Lens | Question |
|
|
|---|---|---|
|
|
| 1 | `repo-better-solution` (a whole pass gating code blocks) | the right mechanism, layer and commit shape? compatible with what users have? |
|
|
| 2 | `repo-functional` (a whole pass gating code blocks; the `browser` or `browser+supabase` environment) | in the running application, does the range do what its code and messages say, and does it leave the flows next to it working? |
|
|
| 3 | `repo-bug-prover` (code blocks, static; with the database a proof may also run there) | a demonstrated defect in the range's code |
|
|
| 3 | `repo-security` (code blocks, static, beside the bug-prover) | a demonstrated gap in a protection the repository promises |
|
|
| 4 | `repo-facts` (message blocks, documentation blocks and the `whole:order` / `whole:absent` slots, static) | every claim in every commit message, and in the documentation the range adds or changes, proven or gone |
|
|
| 5 | the owner | approves the exact frozen hash, then the push happens |
|
|
|
|
crafter-review's prompts, reuse keys, exit codes and shared files are pinned byte for byte by its goldens (`tests/Goldens`): a review one driver started, another resumes and reuses, and every driver on a machine sees the others' passes on its seat board.
|
|
|
|
## Install
|
|
|
|
crafter-review ships as a Flatpak with its own git, Node (for the Playwright MCP server) and ripgrep, so every machine reviews with the same tools:
|
|
|
|
```bash
|
|
flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo
|
|
flatpak install catcrafts net.catcrafts.CrafterReview
|
|
alias crafter-review='flatpak run net.catcrafts.CrafterReview'
|
|
```
|
|
|
|
`master` builds are the `beta` branch (`net.catcrafts.CrafterReview//beta`). The alias is for people: the `/prepush` skill spells every command as `flatpak run net.catcrafts.CrafterReview …`, because Claude's Bash tool sees no aliases.
|
|
|
|
What the machine provides:
|
|
|
|
- **The Claude Code CLI.** It is proprietary and not bundled. crafter-review looks for it in this order: `CLAUDE_BIN` or `REVIEW_GATES_CLAUDE_BIN`, the PATH, the editor extensions' bundled binary (newest first), then `~/.claude/local` and `~/.local/bin`. It runs inside the sandbox, so every pass is measured and every pass's process tree can be killed.
|
|
- **Seats.** The lens processes run on the seats in the repository's `.env` (`CLAUDE_CODE_OAUTH_TOKEN1..6`, `ANTHROPIC_API_KEY1..6`), else this machine's own login. `crafter-review seats` probes and reports them. It never prints a token. An account whose 5-hour limit Anthropic has removed shows `no limit` in that column and is placed by its weekly window. Every account takes at most 128 passes at once, every driver's counted, and there is no machine-wide cap beyond that and free memory (`run --concurrency N` sets one).
|
|
- **Chromium, for a lens with the browser.** Run `crafter-review browser install` once. It installs the Chromium build the pinned `@playwright/mcp@0.0.83` drives into `~/.cache/ms-playwright`, where the MCP server looks. It runs Playwright's own installer, which also removes the browser builds no installed Playwright on the machine refers to.
|
|
- **A local Supabase stack, for a lens with the database** (`supabase start`, or the repository's own bring-up). The `sql` helper speaks the Postgres protocol itself: no `psql` and no driver to install. Every call is one transaction, rolled back.
|
|
- **gpg, if your commits are signed.** `freeze` reads each commit's signature through gnupg's keyboxd socket, which is all the sandbox is given. The agent, which signs, stays out of reach.
|
|
|
|
## Setting a repository up
|
|
|
|
From the repository's toplevel:
|
|
|
|
```bash
|
|
crafter-review init
|
|
crafter-review config # edit .review-gates/config.json until it prints no problem
|
|
```
|
|
|
|
`init` does four things:
|
|
- it writes `.review-gates/config.json` from the template when there is none - every lens off: turn on the ones this repository runs;
|
|
- it adds `.review-gates/push/` and `.review-gates/trees/` to `.gitignore`;
|
|
- it writes the `/prepush` skill to `.claude/skills/prepush/SKILL.md`, which a later `init` refreshes (the line after its frontmatter marks it as init's);
|
|
- it prints the next steps.
|
|
|
|
It writes nothing in a subdirectory, in your home directory (`--force` when that is the repository) or over a `SKILL.md` it did not write.
|
|
|
|
`.review-gates/config.json` is committed. It says what the pipeline must know about the repository:
|
|
- who may author its commits, and which branch a range is measured against;
|
|
- which lenses run (every lens is off until `lenses.<name>.run` is true) and the environment each gets (`none`, `browser`, `supabase` or `browser+supabase`);
|
|
- how its application is started and reached: one stack, or several side by side (`slots`);
|
|
- where its local database answers;
|
|
- which paths carry no runtime behaviour.
|
|
|
|
`crafter-review doc config` prints the template; `crafter-review doc prepush` prints the procedure, `process/prepush.md`.
|
|
|
|
### Moving a repository off the review-gates submodule
|
|
|
|
1. Re-freeze every review in flight with crafter-review. The Flatpak's git may be another version than the one that froze it, and a different git version gives a different hash. The old `run-line.txt` also points into the submodule.
|
|
2. `git submodule deinit -f review-gates && git rm review-gates`
|
|
3. `crafter-review init`. If `.claude/skills/prepush` is the old link into `review-gates/`, it removes the link, never writing through it, and makes a real directory with the skill.
|
|
|
|
`config.json` keeps working unchanged. So do the reuse keys: a range reviewed before the move is reused, not bought again.
|
|
|
|
## Running a review
|
|
|
|
```bash
|
|
crafter-review freeze --slug <branch> # Gate 0: checks, export, hash, run line
|
|
# start the application / the stack from the checkout at HEAD, when a lens has the browser / the database
|
|
crafter-review run --artifact .review-gates/push/<branch>/ --hash <sha256> \
|
|
--base <sha> --trees . --dispositions .review-gates/push/<branch>/gate-state/dispositions.json --date <date>
|
|
```
|
|
|
|
`freeze` prints the exact run line and writes it to `run-line.txt`; launch from there in a detached shell (`setsid nohup … &`). The exit codes:
|
|
- 0: SEND-READY (push-ready);
|
|
- 1: FIX FIRST;
|
|
- 2: INCOMPLETE, PARTIAL or CONFIRMING (run the same line again);
|
|
- 3: preflight.
|
|
|
|
The dump with every finding, note and coverage entry is `gate-state/commits-<hash8>-run-<n>.json`.
|
|
|
|
### Reviewing another repository
|
|
|
|
A directory can review repositories other than itself: a workbench whose config, `.env` and records serve several. From it, `crafter-review freeze --repo <path> --out <dir> --worktree`, then the printed run line. The run reads the repository under review from the artifact's `freeze.json`: the prompt names it, the calibration is kept under it, and `--trees` defaults to the recorded worktree. The config, the env file and the slots are the working directory's, the lenses run in it, and `--context-paths` are relative to it.
|
|
|
|
### The kernel modes: a patch series, a mailing-list reply
|
|
|
|
Beside the push gates, crafter-review runs the pre-send review a kernel contributor's workbench keeps: `--mode series` over a `git format-patch` export with its cover letter, and `--mode reply` over a mail draft (a reply's gate-state sits beside the draft). Both take `--subsystem <name>`, whose journal - `journal/<name>/ledger.md`, `captures/`, `captures/threads/` - is the lenses' evidence, and the trees `--trees` names (default `work/linux,work/milos-linux`; history only in the full clone). lore.kernel.org is reached through `python3 utilities/lore.py` in the working directory, which also prefetches every reference the artifact cites into `gate-state/refs/` before the gates run. The series gates are structure (better-solution), code (code-quality, minimalism, bug-prover) and artifact truth (facts, consistency, process-docs, etiquette, conciseness); a reply runs artifact truth alone. `--reviewers "A B,C D"` adds the persona gate after them: one instance of the maintainer-persona lens per reviewer, calibrated on their own lore mail, its whole pass gating its blocks and pruning the units that are not theirs (`--persona-blocks all` keeps them). Gates clean without a persona pass is GATES CLEAN, not SEND-READY. These lenses are off until the config turns them on, like every other; the effort defaults to max, and a reply's units are read once.
|
|
|
|
To stop a run, run `crafter-review stop <its scratch dir>`, or `crafter-review stop --hash <hash8>` for every live run of a hash. Its running passes are killed, no later gate starts and the dump is written. A run takes its passes with it however it ends, SIGKILL included: each pass runs under a supervisor that kills its whole tree, the MCP server and Chromium included. `flatpak kill net.catcrafts.CrafterReview` is the hard stop.
|
|
|
|
| Command | What it does |
|
|
|---|---|
|
|
| `freeze` | Gate 0: the checks, the export, the hash, the run line; `--verify` at push time |
|
|
| `run` | the gates of the mode (`--mode commits`, `series` or `reply`): the lenses, the dump; `--dry-run` launches nothing |
|
|
| `slots` | how many stacks fit in free memory, and the commands for the missing ones |
|
|
| `config` | the merged config and its problems |
|
|
| `seats` | every seat probed: both windows, their resets, the drivers running |
|
|
| `init`, `doc`, `browser install`, `stop` | the setup, the shipped texts, the Chromium, a stop |
|
|
| `sql`, `totp`, `inventory` | the helpers a lens runs, and the partition of an export, a series or a reply |
|
|
|
|
## Inside the Flatpak
|
|
|
|
The sandbox gets the network, the whole host filesystem (`--filesystem=host`), the host's `/tmp` and gnupg's keyboxd socket. `--filesystem=host` is a convenience for a developer tool, **not an isolation boundary**: a lens's tool allowlist is what keeps a pass to its job, as it was outside the Flatpak.
|
|
|
|
The driver hands its children the host's XDG directories. git then reads `~/.config/git/config`, Playwright finds `~/.cache/ms-playwright`, and claude and npx behave as on the host. A pass's shell is bash. A few things do differ from a native run:
|
|
- a lens reading `/usr` sees the Sdk's;
|
|
- git hooks run with the Sdk's tools;
|
|
- `FLATPAK_ID` reaches every child;
|
|
- a runtime update can change git between `freeze` and `--verify`, which the MISMATCH message then names.
|
|
|
|
**Several drivers on one machine.** Every driver registers its passes on one board, `~/.cache/review-gates/seat-board.json`, and holds a lifetime lock beside it while it runs. A driver in the Flatpak publishes the host pid of its sandbox, so a driver outside it sees it alive exactly as long as it runs and sweeps it the moment it is gone.
|
|
|
|
## Building from source
|
|
|
|
```bash
|
|
crafter-build --local test # the suite; --local takes ../Crafter.Network instead of the pinned commit
|
|
crafter-build --local lint
|
|
crafter-build run flatpak # the Flatpak on the stable Crafter.Build base (flatpak-builder or org.flatpak.Builder)
|
|
crafter-build run install # install that build as net.catcrafts.CrafterReview//local
|
|
```
|
|
|
|
`run flatpak --base=<branch>` builds on another branch of the base app, and `--deps=local` takes Crafter.Network and Crafter.Thread from the checkouts next to this one, for commits not yet on forgejo. `AGENTS.md` is the guide for changing the pipeline.
|
|
|
|
## License
|
|
|
|
crafter-review is licensed under the GNU Affero General Public License v3.0 only (`LICENSE`). It vendors [nlohmann/json](https://github.com/nlohmann/json) 3.12.0 (`lib/json.hpp`, MIT License, © 2013-2025 Niels Lohmann).
|
|
|
|
## Copyright
|
|
|
|
Copyright (C) 2026 Catcrafts®
|