Skip to content

Getting started

Rennet reads a branch or a pull request and drafts it into boards you can actually read. What you raise while reading becomes one GitHub review, a work order for a coding agent, or the pull request itself. This page walks that loop once, end to end.

If Rennet is not on this machine yet, install it first.

On a new client with no projects, Rennet opens a full-window welcome. Choose Start to gather the floating code into the Rennet logo and reveal appearance choices beside it. The constellation changes with each setup step. The full-screen background persists across every step and animates automatically. Your operating system’s reduced-motion preference stops background motion and the rotating welcome phrase. Without WebGL, the welcome uses a static wordmark. It introduces the review model, applies appearance choices immediately, shows the tools detected in this environment, and lets you choose the orchestrator and Dual Harness mode. The orchestrator is the chat thread a review runs on — the one in the chat column. Choosing it enables that harness for reviews on this machine and routes the conversation to it, so a machine with both Claude Code and Codex runs the chat on the one you picked; the model council picks the model it answers on. The lens boards route on their own. Dual Harness starts on when both are available.

If neither harness is detected, install Claude Code or Codex and check again. The welcome does not replace the contextual onboarding tour; coach marks begin after setup as you reach the controls they explain.

The Access step offers Grant Full Disk Access on macOS. It opens System Settings → Privacy & Security → Full Disk Access. This is optional: without it, macOS may prevent Rennet from reading external drives, network volumes, or protected folders. You can continue setup and change it later.

The welcome does not ask you to add a project. On Ready, Start a new chat finishes setup and opens New Chat. Choose a project when you need one.

The welcome is not a one-time event you can only see on a clean install. Open Settings → Appearance → First Run and choose Replay the first-run welcome, or run the same action from the command menu (⌘K). It reopens immediately, over whatever you were doing, on a client that already has projects. There is no confirmation, because nothing is destroyed: your projects, appearance, and sessions are untouched, and finishing the welcome puts it away again.

A replay follows the same setup steps without adding or selecting a project. Start a new chat finishes it and opens New Chat. Replaying the welcome does not re-arm the onboarding tour, and replaying the tour does not reopen the welcome.

You do not need the window to run a review. With the daemon running (rennet serve, or the desktop app open), rennet review opens one over the same front door a New Chat click uses and reads the result back from a path:

rennet review main..feature/x # a range in the current repository
rennet review --pr 128 # a pull request by number

It resolves the repository at the given path (the current directory by default), adds it as a project if it is not one yet, and prints the review as the daemon produces it: the capture step, each lens lane as it settles, and each board write as it lands, one plain line each.

Both forms are scoped to the repository you run them in, which matters in a workspace project that holds several repositories: a branch name and a pull request number are each unique only within one repository. rennet review --pr 128 reviews repository #128 of the checkout you are standing in, and refuses (naming the candidates) rather than guessing when two of the workspace’s repositories both carry a #128 and neither is this one. The repository identity comes from the daemon, so the CLI never has to spell it. rennet review needs a daemon new enough to resolve that identity (Rennet server 0.1.5 or later); an older daemon is refused at connect, naming the version to update to, rather than reviewing against the wrong repository.

When the boards settle it writes a single JSON document (the review, the range, a per-lens lanes array giving each lens’s outcome, and every lens’s board or its typed absence) to <data dir>/reviews/<reviewId>.json (or a path you pass with --out), and prints that absolute path as its last line, so a script can take tail -1. The path is always absolute, even when --data-dir is relative.

The exit code carries the outcome: 0 when the boards settled, 1 when the daemon was not running or the review failed, was cancelled, or ran past --timeout (default half an hour). Once a session exists, a document is written whichever way it ends, so a script always has something to parse: a failure, cancellation, or timeout writes the review document with its reason, and even a capture-stage failure (before a review id exists) writes a small failure document, keyed by the session id, carrying capture: "failed", the outcome, and the reason. Errors BEFORE a session is created write no document and print only to stderr: usage errors (2), a missing or incompatible daemon, and an invalid checkout or pull-request target. A review opened this way is an ordinary session: open it in the app by its id afterwards and its boards and transcript are where you left them.

Add Project in the sidebar opens a dialog with two parts: a source picker and a directory browser.

The source picker lists every environment Rennet can reach — this machine, each WSL distro on Windows, and any environment you have paired — and ends with an Add Environment escape into pairing. Switching source reloads the browser against that machine’s own filesystem, so browsing a distro or a paired machine works exactly like browsing locally.

Click a row to descend, use Up or Backspace to ascend, or type an absolute path and press Enter to jump there. The path bar shows the current folder with a trailing slash, so appending the next folder name is one keystroke away. Arrow keys move between rows. A folder holding a repository wears a repo badge; a folder Rennet cannot read is dimmed and cannot be entered. Add stays inert until you select a folder.

Hidden folders (names starting with a dot) are out of the list by default. The eye button in the toolbar shows them, and the choice sticks across dialogs. On this machine the desktop app also offers Browse…, which opens the system folder dialog and jumps the browser to whatever you choose there. It does not appear while browsing a WSL distro or a paired environment, because the system dialog can only see this machine’s files. There is no recents list.

Pair a new environment with Add Environment: it takes an address and a one-time code. Run rennet pair on the other machine and it prints a link that fills both fields.

Adding a project lands on its processing view. A scout reads the git remotes, checks for issue-tracker markers and CI config, reads the README, contributing guide, and any agent instruction files, then reports how many answers it detected and how many it guessed. The structural map is built when the scout returns. The header status reads scouting, then indexing, then indexed.

While the map builds, a prefilled questionnaire offers the project’s setup for a look: issue tracker, default branch, check command, and the project’s mark. The check command is what a coding round’s work order asks its agent to run before committing; Rennet never runs it itself. Every answer carries a chip reading detected or guessed — the value, provenance, and evidence line come from the scout record Rennet just saved, rather than from canned UI defaults. When the scout finds a logo in the repository, Rennet copies it in and the project shows it straight away — no click. You can override that in Settings → Projects → Identity: pick one of the fixed glyphs, upload your own image, or ask Rennet to detect again. Your pick wins over a later detection, and the mark stays cosmetic — it never enters agent context. Answer the questionnaire or skip it — the map finishes and the project works either way.

When the map is built, the processing view shows a Project Ready summary — its scope and file counts — and a full-width Start a Review button beneath it that carries you into New Chat for the project you just added. That ready state appears only once the run’s last phase has settled; the same boundary clears the project’s sidebar spinner, so an indexed header never sits above a running timeline. Start a Review is still offered after a failure, because a rough index never blocks you.

Nothing here calls a model to summarize your repository. The map is read off the tree: files, packages, entry points, exported symbols, and imports. When a review runs, each lens is a coding agent started in the reviewed checkout — it reads the code itself, with the tools it would have anyway.

The processing command has a stable identity and stores scout and structural-map checkpoints beside the map. Reopening the view reattaches to that run. If the daemon restarts, it resumes the first incomplete phase and reuses completed work instead of duplicating progress rows.

The project remembers which machine it lives on and reconnects there when you reopen it. See Windows and WSL for distro requirements.

On macOS, the first run also detects which coding agents and forge CLIs you have installed, by asking each one for its version. If one of those binaries is an old build, macOS may show an XProtect warning about that program. Rennet bundles no harness binary and reads nothing but the version each one prints — the warning is about your own installed CLI, and updating it clears the notice.

A session is one conversation — its own thread in the chat panel — and everything hanging off it. It claims exactly one review target: your branch, your PR, or a teammate PR.

New Chat in the sidebar (⌘N) opens a searchable project picker. Choosing a project shows that project’s branches and pull requests in one list. Local branch rows appear immediately; pull-request rows join as each repository finishes loading, and the progress names the repository being read rather than guessing a percentage. If the project’s forge is unreachable, local work stays available. Until that first read answers, the list says it is scanning — a project on a network mount can take minutes, and an empty list mid-scan is not the same claim as a project with nothing open. The filters (attention, ownership, local branches, pull requests) sit in a rail beside the list on a wide canvas and fold into a row above it on a narrow one. Above the list, the search box takes the width; beside it sit the facets and Refresh. A facet (Author, CI, Repository) is a multi-select over that column’s values, drawn only when the list holds more than one, so a single-author project shows no author facet and a single-repository workspace shows no repository facet. Author, Lines, Files, Created, and Activity are sortable column headers. The list opens with none of them active: its rows are ordered by what needs you first, then the ones you own, then the most recently active, so what is waiting on you clusters at the top instead of scattering through an activity sort. Clicking a header takes over with that column’s own sort, and the sorted one carries its arrow. The rail, the facets, the search, and the sort compose into one list, and when any of them narrows it a line under the table says how many of the rows are showing, with Clear filters beside it. Refresh re-reads the project’s branches and pull requests in the background; the rows stay on screen while it runs, and the button spins until the read answers. Show merged PRs adds faded historical rows to the same list; the open rows stay on screen while the merged pages load, with a line in the list saying so, because a repository with history takes several seconds to page through.

Every row leads with an identity column: the row’s icon and one lowercase word naming what it is and how it touches you — review for a pull request whose review is requested of you, your PR for one you opened, PR for a teammate’s, merged or closed for a settled one, and local for a branch with no pull request. The word sits in a fixed leading lane you read straight down, so a row’s kind and your relationship to it are never something you hunt for beside the title. A single gold left edge marks the rows that need you — your review was requested, or your own open pull request has failing CI — and nothing else carries it, so gold means one thing here. The edge never stands alone. Where your review was requested, the row also shows the review word and the request icon; where it is your own pull request with failing CI, it shows the your PR word and the failing-CI mark. Either way a screen reader hears why the row needs you, so the edge always has a spoken companion.

The middle columns are the same for every row: the change (a pull request’s title and number, a local branch’s name), the author with their forge avatar (your local branches wear your own), CI as a green check, red cross, or copper dashed ring, lines added and removed, files touched, and when the change was created. A pull request’s numbers come from the forge. A local branch’s are measured on your machine, against the newest commit your clone holds for the project’s primary branch — origin/main or main, whichever is ahead: its committed diff against that commit, and the date of its first commit past it. Rennet reads the refs your clone already holds, so a fetch is what moves those numbers on. A branch that is not ahead of the primary branch has nothing to review yet, so those cells read ”—” rather than zero. Uncommitted edits are not counted in the lines. GitLab does not report line counts in its merge-request list, so GitLab rows show ”—” there.

A Local column states what is on your disk. A checked-out worktree says clean or dirty and how many commits it is ahead of and behind that same primary-branch commit; a bare branch with no checkout says only ahead and behind, because there is nothing to measure for cleanliness. A pull request you also have checked out locally reads checked out here, with a copper dot when that checkout is dirty; one you have not shows an em dash. As the canvas narrows the list folds from the right: files and created go first, then CI, the Local column, and the author’s name, leaving the identity, the change, the author’s face, the lines, and the activity. The back arrow or Escape leaves New Chat for the surface you came from. When the filter contains text, the first Escape clears it and the next leaves.

Clicking a row starts the session — it is not a selection you then confirm. Rennet mints the session, claims that target, and takes you into it immediately.

You land on the boards. There is no waiting screen: the review workspace opens at once, inside the same shell as everything else, with the sidebar, the session top bar and the chat column already around it. The chat’s own thread is being made while the capture runs, so the column says so until it is ready rather than looking empty.

What is still happening to the boards is said in a line across the top of the workspace, over the boards rather than in front of them. While the change is being captured it names the step it is on — resolving the repository, then capturing the change — with cancel beside it. While the lenses are drafting it says so. When nothing is happening, the line is not there.

All five lenses are on the rail from the first second, in the session top bar, each in its own colour — Flagged red, Decisions yellow, Design blue, Sequence green, Noise neutral. The colour tells you which lens. How that lens is doing is the rule under its tab, and it is a shape, not a colour: a faint rule means nothing has been drawn yet; a dashed rule with a lamp travelling along it means the seat is writing right now; a solid rule means the board is cut clean; a rule split by a seam means that lens came back with something changed; two offset pieces mean the seat failed; a dotted rule means the lens had nothing to say. Flagged carries one working mark per voice, because it runs two review passes. Noise waits for the other four and says which ones it is waiting on — its board is whatever they did not cite, so it cannot start until they finish.

Each tab shows its icon and name. Noise is unavailable while the other lenses are running. Hover or focus its tab to learn why it waits. Once they finish, you can open Noise while it reviews the remaining change.

A board draws itself while you watch. Its main heading names the lens, with the generated title beneath it. Animated rings on the tab and heading show work in progress without interrupting the document. Changes since the previous round remain hidden until the board settles.

While the selected lens is generating, its activity popover appears automatically under that tab. Hovering another tab temporarily shows that lens’s activity; leaving it returns to the active generating lens. On completion, a brief status animation plays and the automatic popover fades away. A popover you are hovering or focusing stays open until you leave or dismiss it. Hover or focus a tab to read its activity again. It shows a concise current action, recent activity and how long you have been following that lens. Open transcript stays visible and becomes available once the agent thread starts. It shows the selected agent’s full conversation in a drawer beside the board. Your own chat stays in place. Choosing another lens moves the board and open transcript together, and opening a transcript from another lens’s tab selects that lens, so the transcript always sits beside its own board; opening Diff closes the transcript drawer.

While the change is being reviewed there is no Continue yet; it appears in the bottom-right corner when the review is ready. The Rennet mark in the top-left corner is what animates while Rennet works — in the sidebar’s lockup when the sidebar is open, and as a floating orb in the corner when it is collapsed — so nothing repeats that over the boards. A floating Cancel button sits in that bottom-right corner while the boards are being generated. If a generation fails or you cancel it, a header appears over the boards with the reason and a Retry.

You can navigate elsewhere while a review runs. Its sidebar row keeps an animated ring. Completion briefly shows a checkmark, then a dot until you open that review. Each review has its own indicator even when branch names match. Failure shows its reason on the row, and cancellation clears the running indicator.

The board region scrolls, so a long board on a large change is readable end to end. You can leave without stopping the work, or cancel and retry it in place. A failed capture or board generation keeps the session and names the failed stage instead of dropping the review. The target picker has no composer; project-wide conversation remains in the orchestrator chat beside it.

What gets captured depends on the row. A pull-request row opens that pull request’s diff. A local branch row captures that branch’s own commits — everything since it left the newest commit your clone holds for the project’s primary branch — without checking it out. Nothing on disk moves, and you can review a branch you are not standing on.

That difference matters once you are reading. A working-tree capture is watched: edit the repository and the review says it went stale, and offers to regenerate. Only your edits count. Rennet keeps each board’s own storage under .rennet/boards/ in the repository, and that storage is excluded from what a review captures and from what the watcher compares, so a board Rennet writes never makes the review it belongs to look out of date — including after quitting and reopening. Anything else you keep under .rennet/ is your project’s content and is reviewed like any other file. A branch or pull-request review is a snapshot of fixed commits, so it is not watched and never claims to have gone stale — there is nothing for it to drift against.

A branch with no commits of its own — already merged, or identical to its base — opens as an empty review. That is the honest answer rather than a click that appears to do nothing.

A claimed target leaves the list, so two sessions can never fight over one branch; clicking the same target again returns you to the session that owns it rather than starting a second. Sessions nest under their project in the sidebar, each leading with the target icon its claim proves — a branch glyph, or a pull-request glyph once the session claims a PR. Whether a teammate authored that PR, and whether its review is waiting on you, are not facts the session record carries, so a row states neither rather than guessing; those states arrive with the source that can answer them.

The source list does carry those states. A row reads Needs you when a review request or failing CI needs attention, Reviewed once its local review stage has completed, and Merged after its pull request closes. The state label wins over the more general owner label.

Once boards exist the target is locked. Reviewing something else means a new session.

Right-click a session for Pin, Rename, or Archive. Pinned sessions rise to a Pinned section at the top of the tree; archived ones move to Archived at the sidebar’s foot. Sessions are records: reopening one restores its transcript, its boards, and everything you staged, including generations frozen by earlier rounds.

Right-click a project for Rename. The name edits in place: Enter saves, Escape cancels, and an empty value restores its org/repo name. Renaming does not move the current route because navigation keeps the project’s stable identity rather than its display name.

Each lens is its own board. The session top bar centres the lens rail and keeps the boards in the order shown below. Every tab carries a coloured stop along its foot, in that lens’s own colour, at full strength on the tab you are reading and dimmed on the rest. The stop says which lens, and how it is cut says how its seat is doing; which one is selected is still said by the raised fill and the ink, so the rail reads the same with colour ignored. The session URL owns the selection: Flagged is the address-free default and another lens uses ?lens=. A completed frozen generation without Flagged falls back to its first available board and replaces the URL with that honest address. A live generation keeps the requested address while its boards arrive. Selecting a lens from History or Diff returns to its board. Reloading the URL opens the same selection.

BoardQuestion
DesignWhat was this change supposed to do, according to its specification or its author?
SequenceIn what order should I read the implementation?
DecisionsWhich implementation choices need explanation?
FlaggedWhere did automated analysis find a problem or a disagreement?
NoiseWhat remains, and why may it need less attention?

The rail carries every lens, always, and none of them is ever a disabled segment. A lens with nothing to show says so on the board — a typed absence in its own words, or the reason it failed — rather than leaving a gap where a tab used to be. Reviewing a proposal before any code exists gives you a Design board and four lenses that say they found nothing to draft.

Design reads the specification the branch was written against, when it has one. When the branch itself touches one in a format Rennet parses — an OpenSpec change, a Kiro feature, BMAD documents, a superpowers spec or plan, an ADR or a grill-me CONTEXT.md — Rennet renders that specification’s own text straight onto the board, with no model turn and nothing sent to a provider. Otherwise a model reader looks through the checkout where specifications live, using the branch’s own commit messages and pull request body as the clue, and it cites the line that ties the document to the branch so you can check the link. When there is no specification at all, the Design board is an overview drafted from the pull request description, the documentation the branch adds or changes, and the issues it links. The board labels itself an overview rather than a specification, and every part of it names the file it was read from. Only a branch with none of those three reads “No spec found for this branch.”, which is a result rather than a gap.

The board drafter writes each title and short intro. Design uses a wider structured measure for specification content. Sequence, Decisions, Flagged, and Noise use a narrower reading measure. A folded section lists its child headings, or its first paragraph when it has none, and unfolds to its contents; every board opens folded, so you take the previews first and open what you want to read. Folded counts name review objects: findings, decisions, requirements, steps, outcomes, groups, files, and comments.

Flagged is the one board whose sections do not fold. Each section is a heading over its findings, and each finding is its own fold: severity, the claim, and the concurrence pill read at a glance, and the row opens to the scenario, the fix, and the cited code. A title on any board renders code names in backticks as code and never shows markdown emphasis marks.

Code is cited, never copied. A code block card carries the file path and the exact line range and hydrates the real lines from the captured patchset, so numbering cannot drift from the code under review. Long lines wrap inside the card rather than scrolling sideways. When that path belongs to the active captured patchset, clicking it opens Diff on the file and preserves the other session query state. The card’s header carries its controls: Expand context widens the excerpt, Full file shows the whole reviewed file and Cited hunks returns to the excerpt, and the card adds View test or View implementation when the reviewed tree relates the two files, by import or by name; an unchanged test opens inline from the reviewed revision with Back to review in the same header, and several matches offer a chooser. In prose, a path:line citation is a chip: click it and the real lines unfold below the paragraph; click again and they fold away.

Click an identifier in the current diff or a code card to inspect its definition and references. Tab enters the code region; arrow keys move between identifiers, and Enter opens one. Each location can open in your editor; Escape returns focus to the identifier. The inspector labels structural matches and textual guesses. Deleted rows use the base revision; added and context rows use the reviewed revision. Uncommitted edits and historical patchsets are not indexed by this lookup.

A finding reads as flowing document text, not a boxed card: a severity chip, the claim as its title, a concurrence badge, then the body and the proposed fix as its own callout. The badge reads “concur 2/2” only when both review seats raised the finding at comparable severity; “severity split” when both raised it but disagreed on how much it matters; the seat’s name and “only” when one raised it and the other did not; and the seat’s name alone, quietly, when a single harness ran and there was no second opinion to compare. Request This Change stages that fix for the hand-off; the same control becomes a Staged · Request Change receipt and unstages it when clicked again.

Beside the switcher sits the Diff pill. It opens the raw patchset in the familiar files-changed shape: a filterable file tree on the right, per-file cards with unified hunks and dual line-number gutters on the left, a summary line reading files changed with total additions and deletions, and a per-file Viewed checkbox that collapses the card and ticks the tally. Diff is not a board — it is the raw source, always one click away. Large patchsets keep the summary and complete file tree responsive while Rennet windows the file cards and diff rows; choosing any file still lands on its exact virtual position.

Three routes, one result.

Highlight board prose. A toolbar appears above the selection with Comment, Request Changes, and Explain. Comment opens a small editor quoting the span; ⌘/Ctrl + Enter saves. The quoted text keeps a durable highlight afterwards — click it to reopen the thread, read the replies, and add a follow-up. Explain asks the session’s chat thread a question; it is not review content and never counts toward an exit.

Comment on a line. Hovering a line of code turns its line number into a +. Click it for an editor offering Save, Request Changes, and Delete. The same editor serves board code, the Diff view, and code in the chat — a comment made on one surface is the same object everywhere. Diff line comments key to new-side line numbers, so a requested change carries a real diff position.

Say it in chat. The chat column beside the surface is the review’s chat thread, and it travels with you across every board. It is open when you arrive — it holds the conversation about this review, not an optional extra panel — and ⌘J closes it if you want the room. A close is yours: it stays shut, through every navigation, until you open it again. Ask it about the change and it runs a real turn on your own installed harness, working in this review’s checkout, and streams the answer back as it arrives. The thread persists in the sidecar, so it is still there after a reload. Asking about a highlighted span sends your question with the cited lines into the same thread and opens the chat on the answer. The thread knows it is this review’s conversation, and it can use Rennet the way you do: read the boards, stage asks into your composer, compose a hand-off, dispatch a round. Staging is what it is steered toward, because a staged ask is what Rennet tracks — an ask it stages carries the thread as its author, joins the basket beside the ones you stage from a board, a line, or a highlighted span, and leaves only through the exit you click. Unstage it like any other ask, and the basket names it as coming from the chat thread.

A session you opened before this release keeps the thread it already has: a thread’s briefing and its tools are fixed when the thread is created, and Rennet never rewrites one under you. Archive that session and reopen it to get a briefed thread.

If the thread is not there yet, the column says which kind of “not there” it is, and it never claims something is coming when nothing is. A skeleton placeholder is a real wait — the thread exists and is arriving. This review has no thread, and none is being opened is settled, and carries the reason Rennet could not open one. This review’s thread is no longer in the chat sidecar means it was deleted. On a chat-only session — a new chat before its capture attaches — the column simply says no review is attached, because there is nothing to open a thread for yet.

Clicking a file the thread mentions opens it in Diff, Rennet’s own viewer, at that file — not in a second file pane inside the chat. A file the review never captured is not a Diff destination, so those still open the chat’s own viewer rather than moving you nowhere.

An ask is the staged unit of the hand-off: text, an intent (comment or request change), provenance back to the finding, comment, or thread it came from, and a code anchor where one applies.

Findings never stage themselves. Staging records your judgement, not the board’s output, so every staging control is also its own receipt and its own undo — press Request This Change and it becomes “Staged · Request Change ✓”; press it again to unstage.

The gold button at the bottom right of the surface is the basket. It reads Write Review on a teammate PR and Continue on your own branch or PR, and carries one red count: staged asks plus the comments and threads not yet claimed by one. Explain threads never count. Undo of any kind takes the count back down.

The gold button opens the hand-off view — a view over the main surface, exactly like Diff, and the back arrow leaves it. What it offers depends on the target.

On a teammate PR there is one lane: Post Review.

The verdict is a three-way control — Approve, Request Changes, Comment — proposed from what you actually did, with the arithmetic stated beside it (“proposed from your review · N request changes · M comments”). It is always flippable, and an overridden verdict says so and offers “use proposal” to go back. With no asks, Approve is the proposal. Its draft opener is grounded in the active review evidence and your durable acts, not an empty shell.

The draft renders exactly as it will post, so there is no separate preview stage: the exact review body and line-comment threads. Ask provenance and any flattening ledger remain visible beside that post descriptor. The signing view is read-only. Steer the underlying asks with Revise, Drop, and Explain before opening it; if the preview is wrong, return to the review, revise the durable ask, and reopen the signing view to compose new bytes.

A residue line states the bare count of threads and code comments that stay local. One Post Review action sends it, under your name, as one review pinned to the reviewed commit. The posted state names the PR, the verdict, the line-comment count, and links to the review on GitHub.

If the pull or merge request advances before posting, Rennet sends nothing from the stale preview. Review latest revision starts a new review of the same provider target and opens its capture progress; the review you already read stays pinned to its original head and bytes.

On your own branch the hand-off is one goal in two states, and the page’s shape tells you which one you are in.

While asks remain, the page is Changes: one card per ask with its intent, provenance, text, and anchor. The same Revise / Drop / Explain steering works on an ask’s text. Dispatch Round sits beneath the cards. The pull or merge request waits as a single muted line at the foot.

Dispatch becomes available when at least one staged comment or request-change gives the coding worker something to address. Questions and approvals stay with the review; Rennet does not turn them into code work. If the daemon finds no coding work when asked to dispatch, the page stays here, says that no round started, and keeps the staged notes.

Work orders exist on your own branch only. A teammate PR never offers one.

When nothing is left to ask and the description is ready, the page is the change request: the title as its heading, the drafted description rendered beneath, and one provider-named action that pushes the branch and opens it. GitHub shows Open Pull Request and a # receipt; GitLab.com shows Open Merge Request and a ! receipt, using the authenticated glab in that repository’s host or WSL environment.

After the pull or merge request exists, rounds continue exactly as before. There is no separate lane for reviewing your own change request.

A round is one dispatched work order and the successor patchset it returns. Rounds run one at a time; asks you raise mid-run queue for the next one.

An accepted dispatch moves you to the live run: your workspace being opened, the round’s asks being applied, the worker’s activity as a table of steps, the commits, and the round report being drafted and checked. The round works in the workspace this session is bound to and commits on your branch there — Rennet makes no separate checkout for it, and it never stages anything on your behalf: the round’s commits are the ones its agent made.

Rennet does not run your project’s check. The round’s agent does. If the scout found a check command for this project, the work order tells the agent to run it before committing, to commit only when it passes, and to say why in its closing message when it does not — so a failing check reads as the agent’s own account of what went wrong, in its transcript, rather than as an exit code with nothing behind it. If no check command was found, the work order says nothing about one.

Each round runs on its own transcript, separate from the chat you have with Rennet about the review. The round report names the workspace it ran in and the thread and checkpoint the turn left, so you can see where the work happened, which turn produced it, and the thread id its transcript is saved under. Closing and reopening the run, or following its direct link on another launch, resumes from the latest saved step without dispatching the work again.

As soon as Rennet has saved and checked the report, it takes you back to the review and shows that report. It does not wait for every board to finish. If you close or reload the app there, the same report and current regeneration progress come back. If the remaining work later fails, Rennet returns you to the run’s failure screen and keeps the incomplete boards hidden. The session row reads Round N is back only after the round finishes, using its saved ledger number.

The round report is what greets you while the boards regenerate. It states what the round did and where it ran, then lists one item per ask: Addressed, Partial, Untouched, or Beyond the Asks for work the round did that you never requested. Every outcome is verified against the round’s diff rather than taken from the worker’s word, and each item names the ask it traces to and reveals the code where one applies.

The report is measured before it is drafted, and a round whose diff is larger than the report can honestly carry stops there rather than classifying part of it. The failure names the measurement and the limit it passed, and no model is asked anything — Rennet does not summarise a change down to a size that fits and then describe the smaller change as if it were yours. Split the work across rounds.

You read the report while the boards regenerate live beneath it, one lane per lens. Every lens drafts again each round; a lane reads carrying forward when that lens came back with nothing changed, reworked when it moved, and failed with the reason when it produced neither a board nor a trustworthy empty result. In between it reads drafted — the board is written, the comparison not yet run. The lane and the board agree by construction — both read the same comparison — so a lane never claims a lens carried while its sections changed, or while a section it used to have went away. Each lane settles on its own — nothing waits for the slowest lens — so the lanes finish in the order the work actually finished.

The surface never locks. View the New Boards appears only after regeneration has finished and the whole new generation is ready, never as a disabled button waiting to light up.

The new generation shows you the delta by its own shape. Sections the round touched open expanded with a small gold dot; sections that carried forward stay folded to their previews. The dot rolls up to that board’s segment in the switcher and clears for good once you open the section. The previous generation stays readable as a folded drill-down, and Sequence grows a “Round N · Addressed” chapter at its foot, newest last. The selected generation lives in ?generation= in the session URL, so reload, Back, and a direct link return to the same frozen board. With no frozen predecessor, the generation control is absent.

Once a round has completed, a History control joins Diff in the header, making the pill read History · Diff. It lists one row per round with its tally; selecting a round renders its full report. Each modern row states when the round ran, its exact branch or detached target, the coding harness and version that ran it, and the outcome tally from that round’s own report, so nothing you have already read ever vanishes or gets relabelled by a later round.

To inspect a slow or failed round on macOS, choose View → Toggle Developer Tools. Electron opens its standard developer console. Entries tagged [rennet:round] show the review, operation revision, durable phase, report handoff, and each lens status with real timestamps. A classified report adds fixed milestones for turn start, provider settlement, turn settlement, schema parse, evidence verification, and persistence. Those milestones contain only fixed status values and elapsed milliseconds, never code, prompts, model output, diffs, paths, evidence, notes, or provider prose. A terminal operation failure still includes the daemon’s exact local reason, matching the failed round shown in Rennet. Leave the console closed during ordinary use.

Press ⌘P (or ⌘K) to open the command menu from anywhere — the same menu the sidebar’s Search row opens. It filters fuzzily over your sessions, each project’s new-chat entry, every settings page, and the add-project and add-environment actions. Board and diff content is deliberately not searchable from here; the boards are where you read.

Command mode leads with app actions such as Add Project and Add Environment. Raw protocol commands appear only when they need no context and produce a visible result; none qualify today. GitHub fallback cleanup stays in Settings → Environments → GitHub account, which can prove the credential source before offering Disconnect.

ShortcutAction
⌘PSearch
⌘KCommand menu
⌘NNew chat
⌘BToggle the sidebar
⌘JToggle the chat column
⌘,Settings

Use Ctrl in place of ⌘ on Windows and Linux. Settings → Keyboard Shortcuts lists every command with its binding, filterable by name, with a Change control on each row.

Settings takes over the view and leaves by the back arrow or Escape. It has five pages:

  • Environments — one card per machine, with its OS glyph, name, and address or a Local chip. Each card carries the source-control tooling detected there, the coding harnesses detected there, and the model mappings for the review roles — each detected on that machine, so a card never borrows another’s tooling. Rename inline; Reconnect appears only when a machine is unreachable, and re-attempts the connection for real — it says “Connecting…” while it tries, then either the card comes back or it tells you why it did not. Update Daemon appears only when a machine really has a newer daemon to move to, and behaves the same way: it updates for real, then shows the new version or the reason.
  • Appearance — light / dark / system, the interface theme pack, and a separate code theme that applies to every code surface including the diff.
  • Keyboard Shortcuts — every named command and its binding.
  • Projects — scoped to one project: its name and mark, review context, issue tracker, and the guidance rules the review agents read. The name is live — renaming here renames the sidebar row, and emptying it restores the project’s org/repo identity. Worktrees decides where a review works. You set four things: a location — the folder Rennet puts its worktrees in, worktrees/ under its data directory unless you change it; a layout for a branch worktree and another for a pull-request snapshot. Both are written over {repo}, {owner} and {name}; {branch} belongs to the branch pattern and {number} to the pull-request one, because a snapshot has a number and no branch — two grammars rather than one. Each shows the path it resolves to for this repository. And a workspace choice. Share, the default, lets a review of a branch you already have checked out work in that checkout. Own keeps Rennet out of it: it works in a worktree of its own, on a rennet/<branch> branch forked from yours, and the round’s commits land there — your checkout is left exactly as it is, and a Fast-forward action beside the branch carries the commits over when you want them. A pattern naming a token its grammar does not have, or one that would put a worktree outside the location, is refused before it is saved, and the refusal names the token or the escape. Below the controls, Workspaces lists every workspace Rennet has for this repository — its path, its branch, the sessions using it, when it was made and last used, and how big it is — and each idle one Rennet made has a Remove button. If git refuses a removal, you see git’s own words and nothing is deleted. Editors the daemon has no store for render disabled and say so, rather than accepting edits that would vanish.
  • Benchmarks — a switch for benchmark recording (on by default) and the local history of recorded runs, each broken down by stage and grouped by the harness mode its stages actually name. The list is paged and states how many runs it is showing out of how many were recorded. Nothing here leaves your machine.

Every layered value shows a chip naming where it resolved from — builtin, detected, global, or repo — and every section states the file behind it.

Rennet has no backend of its own and no Rennet telemetry service. The daemon and your review state run on machines you control.

Material selected for a review turn goes to the coding harness you chose and to that harness’s model provider, and Rennet records the exact context it assembled. GitHub receives an outbound review only when you post it.

A paired machine connects over your private network. See Remote access for binding, pairing, and path projection.

When a release is ready, an Update control appears at the sidebar’s foot. It opens a dialog listing what the release contains, with Later and Update Now. Rennet never restarts itself without you.

Choosing Update Now or Restart Rennet to update first stops the local daemon that runs from the installed app bundle, then lets the platform updater replace that bundle and relaunch Rennet. The new app starts its matching daemon and reconnects to the durable review state. On macOS, Rennet also arms a small out-of-bundle relaunch helper before ShipIt replaces the app, so a successful install still reopens when the native updater omits its own relaunch. If the owned daemon cannot stop, Rennet stays open, starts its daemon again, and reports the failure instead of closing without installing — so the app you are left with still works. If macOS or Windows rejects the native install handoff after closing the window, Rennet restarts its daemon, restores the window, and shows the updater error so you can retry from the same review state.

Closing the window leaves Rennet in the macOS menu bar or the Windows system tray, with the local daemon and any running review intact. Open Rennet restores the window and reconnects it. When the desktop app owns the local daemon, quitting says so, and an interrupted turn can be retried after the next start. Quitting a client connected to a remote daemon does not stop that remote process.

Public macOS releases are signed with Developer ID and notarized by Apple. The packaged app checks the public GitHub-backed update feed every five minutes until an update is downloaded, then stops checking so the staged install stays installable until you choose it; development and ad hoc packages do not contact the feed at all.