magus v0.4.3 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
8 min read

Reviewing your changes

magus diff reads the working tree's uncommitted changes. To read a committed range instead, name it with --rev base...head.

magus diff

Two things separate it from git diff. Declared target outputs are folded away, because reading a generated file is reading a machine's restatement of a change made somewhere else, so the source edit is the one to read. And what remains is ordered by what it can break, widest reach first, rather than alphabetically.

Reach needs a symbol index. Without one there is no ranking key at all, and diff says so at the top and falls back to path order rather than implying an order it did not earn:

magus graph build

Reading in order

Below the file list, the report prints the order to read the hunks in. A definition comes before its uses, an interface before its implementations, and a test after the code it exercises. Hunks linked that way form a group; larger groups come first, and between equals the one with the wider reach.

Each hunk carries a sentence saying what placed it:

  • starts the group opens a group.
  • uses Parse, defined in step 2 follows the hunk that defines Parse.
  • defines Parse, used in step 3 precedes the hunk that uses it.
  • implements Store, declared above and declares Store, implemented in step 4 pair an interface with its implementations.
  • tests Parse, defined above follows the code the test exercises.

Consecutive hunks from one file share a step. Hunks that use each other share one too. Generated output sits in a folded group (--generated shows it), and a hunk magus cannot place lands in a final unranked group that says why.

The report ends with a completeness line, such as 14 hunks, 14 placed. It names any hunk that is missing or repeated, and any changed file with no hunk to show, so you can tell a whole list from a short one. The same diff and index always give the same order, and no model chooses it.

The terminal viewer walks the hunks in this order, and the console's focus mode shows it one step at a time. -o json carries it as order: groups of steps, each hunk with its why, and count.

It needs the symbol index. When the index cannot be brought current, magus prints no order and a note says to run magus graph build. With the daemon running, stale indexes are rebuilt in the background, so trying again in a minute can work. See Review for how the order is built.

What landing it costs

magus diff --impact

This is the same question magus affected --impact answers, asked of a changeset rather than of a target: which projects rebuild and which were merely edited, who has been changing them, an estimate of the rebuild drawn from recorded run durations, what the workspace's advisors say, which human-authored notes anchor a file or symbol you touched, which compat(until:) markers sit in the files you changed, and what the authors asked magus while writing it.

That last one is the EVIDENCE section. Every other section describes the result; this one describes the reasoning, as the graph queries, explains and lookups an agent ran before it wrote, one line per distinct subject and no answer text.

It needs two things: a host that wires magus agent install, and an agent working under a lease (BAGGAGE magus.lease). The lease is the join, because it is the one identity shared by the process that saw the question and the write it explains, and it holds across an orchestrator and the subagents it hands work to.

The record lives in the cache of the tree the work was done in, so a reviewer reading someone else's branch has none of it. When the list is empty the section names which of the four silences it is: nothing observed here, observations that carry no lease, leases that wrote other files, or authors who genuinely asked magus nothing. Only the last is a fact about the change.

None of it is a verdict. Nothing is gated on it and the exit code does not change. Each section says when it could not measure something, so an empty one reads as "nobody looked" rather than as a clean bill of health.

Keeping a bookmark of what you have read

The REVIEW section reports the two things a reader cannot work out for themselves: files that changed AFTER you read them, and files you have never opened, widest blast radius first.

It is a bookmark, not a score. There is no ratio, and it stays quiet on a small change nobody has disturbed: a count with a target is a count that gets cleared instead of satisfied. It is also never shown to a second person: no team view, no aggregate, no pull-request comment. A read measure someone else can see is a performance metric, and a performance metric gets gamed rather than met.

Record what you read, wherever you read it. Read the files in vim, in your editor, in a pager, whatever you already use, then say so:

magus diff --ack path/to/file.go path/to/other.go

With no paths it covers the whole changeset, and --reason keeps a note with it for the next reader of the report. A receipt covers a file at the content it holds NOW, so editing that file afterwards voids its receipt, which is exactly what makes "changed since you read it" answerable.

magus never infers a receipt from an editor or a session. A measure satisfied by scrolling would launder skimming into review, so --ack needs a terminal, and agent hosts are denied it outright.

Before you push

Reading marks are kept by hunk content, so they answer a question the receipts above do not: which hunks of a range have you not marked read?

magus diff --unread --rev main...HEAD

It narrows the review to the hunks no mark covers: at a terminal the viewer opens on just those, and printed it is the usual report, filtered. It works under every -o. -o name prints one path:start-end per unread hunk, ready for a shell loop, and -o json is the usual document, filtered, with an unread record. It exits 0 whether or not anything is left, because a push held up by a read count would make the count the goal. If the marks cannot be read, it says the read state is unknown and calls no hunk unread.

You do not need a hook of your own to hear about it on a push. The pre-push section magus installs hands off to the check-drift job, which adds one line saying how many hunks of the range being pushed are unread. See the drift notice.

Telling others you are reading

The console's Diff page has a reading toggle for a review that is open on the host. It records a local mark, and for a GitHub pull request it shows the gh pr comment line that tells the others. You run that line if you want to; magus never does. The mark is a notice, and the merge queue ignores it.

If the review merges while the mark is set, magus reports review.merged and clears the mark. The opt-in telemetry metric magus.review.merged_while_reading.duration counts how often that happens and what it cost. See Review.

One thread at a time

A review thread on the host shows with its replies under the hunk it started on, and the report names it there as thread <id>. The report takes those ids from the running server and never asks the host itself, so with no server it lists no threads and names magus server start. To read one:

magus diff --thread 2193847561

The id is the thread id, the id of its first comment, or any reply's. At a terminal the viewer opens on the thread's hunk. With --no-tui, or into a pipe, magus prints the conversation, the hunk, and what the change there reaches: the callers, who it is public to, coverage, conformance and notes. -o json carries the same record. An agent that pairs with you over MCP may leave an outline of up to five short topics beside the thread; you still type every reply. See Review for what the record holds.

Stepping through it in the terminal

At a terminal, magus diff opens the viewer: the same annotations, plus navigation and a way to mark what you have read. Nothing is hidden behind a keypress: the file lines and their evidence render there exactly as they do in the report.

] and [ walk hunks, } and { walk files, v marks a hunk read, . folds the generated files back in, esc returns to the overview, and q leaves. Stepping every hunk of a file earns that file a receipt without a separate --ack.

The viewer joins the same session the console's Diff app and an agent share, so a hunk marked in one is marked in the others.

It stands aside wherever it cannot draw (no terminal, -o json, --watch, a patch argument, --impact) and the report prints instead. That is not a refusal and needs no flag, so a script or an agent is unaffected by the default. To read the report at a terminal anyway:

magus diff --no-tui

Or make it the standing preference:

magus config set key=diff.tui,value=false

Reading a patch you did not produce

Anywhere a patch comes from, - reads it on stdin and a path reads it from a file:

gh pr diff 123 | magus diff -

Both dialects parse: git's diff --git a/x b/x headers, and the bare --- a/x / +++ b/x pair that GNU diff -u and patch speak. A patch magus cannot read is refused rather than reported as an empty changeset, because "nothing to review" is the one wrong answer that costs something: you stop looking.

Staying in git

You do not have to type a magus command to get this. Wire it as git's pager for diff and plain git diff renders through magus, with git --no-pager diff still giving you the raw patch. See Git integration for that, for the other backends, and for why an external diff tool is the wrong hook.

Machine-readable

magus diff -o json

Each file carries read_state, so a script or a Buzz advisor can branch on what has been read without parsing the report.

diffreviewread-receiptsimpactblast-radiustuiackpatchreading-orderunread
Last updated (29d8b3de)
Earlier changes on this page (6)

Full history ↗ · Blame source ↗

Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. Workspace in full.

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. Project in full.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. Target in full.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). Op in full.

Ward

A coded diagnostic that inspects a resolved op and nudges or blocks an anti-pattern before it runs. Ward in full.

Buzz

The language magusfiles are written in (the .buzz engine). Buzz in full.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. Cache in full.

Affected

The set of projects touched by a change; magus affected <target> runs a target only over them. Affected in full.

Daemon

The background process a person starts with magus server start. It serves MCP, the console, background jobs and the warm knowledge graph, and adopts nested magus calls into one pool. Daemon in full.

CI

An ordinary target you compose yourself with ctx.needs; magus does not hardcode its stages. It is the anchor magus affected ci keys off, and ci strips the rw charm. CI in full.

Health

The at-a-glance daemon state derived from the pool: healthy when the pool is reporting, degraded when it reports an error, down when there is no pool. The dashboard color-codes each state. Health in full.

Agent

A model working through an agent host such as Claude Code, Codex, Cursor or OpenCode. magus treats an agent as one more caller of its CLI, MCP tools and console, held to the same checks as a person. Agent in full.

Tool

An action a model can call through its host: a name, a description, a typed input and a result. magus serves its tools over MCP, each a read of the workspace model or a run of the CLI it already has. Tool in full.

Session

An agent host's conversation, by the id the host delivers to its hooks. magus never mints one: a record with no session is unattributed, and the OS user it carries says whose account ran it. Session in full.

Job

The unit of delegated work, and one row of the job store: what an orchestrating agent handed out, with its goal, the checkpoint it was cut against, the paths it may write or must not touch, and the one check it runs. A job's holder is either a session, for work an orchestrator handed out, or the daemon, for its own maintenance. The store records; the agent guard is what reads those facts back when grading a write. Job in full.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate. Run in full.

Lease

The grant a holder takes on a job: the write and read paths that job declared, enforced in the checkout that took it with magus job exec. A job is the piece of work; a lease is permission over it. Lease in full.

Advisor

One read-only check from the advice suite: it reads the changeset through magus and writes one titled section of findings. The same advisors run as a pull request comment in CI and inside magus diff --impact locally. Advisor in full.

Conventions

Placeholders

Angle brackets mark a value you replace with your own; never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.