/Weekly update
The Reflog: week ending August 6
Change review rebuilt around a global queue, stacks land as a first-class primitive, diffs get an order of magnitude faster, and hosting learns to drain gracefully.
Vex is built in the open, on itself. Every change in this post was reviewed and landed through Vex, by the same merge queue and the same change pages the post describes.
This week was mostly about one thing: making the middle of the workflow — the part
between “I have a change” and “it landed” — fast enough and legible enough that you
stop thinking about it. Along the way we shipped four CLI releases, 1.2.2 through
1.3.1.
Change review, rebuilt
The biggest visible shift is that changes are no longer something you go looking for per repository. There is now a global change queue: one place that shows everything in flight across your organization, replacing the old per-repository changes list.
The change page itself was taken apart and put back together:
- A file tree rail on the diff, with viewed marks, so a large review has a spine instead of an endless scroll. Diff file order now matches the tree order — they used to disagree, which quietly made large reviews harder to navigate.
- An activity rail carrying discussion across both the overview and the diff, so a comment thread does not disappear when you switch tabs.
- A triage chip on the change header, and a landing readiness summary that says what is actually blocking.
- The change shell now streams, and the diff lives on its own route, so the page paints while the heavy part is still loading.
Reliability work went in alongside it. A change whose base moves now re-checks its own mergeability rather than showing you a stale answer. A queued change says what it is waiting on instead of leaving you to guess. A landing attempt that retries now shows that it is retrying, and why. And the change page no longer misreports the reason landing stopped — a bug that turned a recoverable pause into what looked like a dead end.
Stacks are a first-class primitive
Stacked changes stopped being a convention and became something the platform knows
about. vex stack landed in the CLI, backed by canonical ref namespaces in the
backend and normalized native jj refs underneath.
Practically, this means:
- Stacks are visible in the web UI, and documented as canonical refs rather than ad-hoc bookmark naming.
- Submitting a stack numbers parents before children, so change numbers read in dependency order instead of whatever order the upload finished in.
- Stacks project to GitHub with proper tracking branches, so a mirrored repository sees the same shape.
- History badges the commits that landed with the change they came from — you can go
from a line in
vex logback to the review that produced it.
The merge queue grew up with them. It can now validate more than one native candidate at a time, re-plans an active candidate when the target branch advances underneath it, and takes proper leases on candidate refs. One change that could not be validated used to wedge the whole queue; it no longer does. Recent queue outcomes are paginated, so the history is actually browsable.
Diffs got fast
Diffs are the most-loaded surface in Vex and they were the slowest. The fix leans on the one guarantee source control offers for free: a commit never changes.
That turned into a chain of work — separating a commit’s mutable half from its immutable one, fingerprinting the patchset diff URL so it can be frozen and cached forever, warming a diff before anyone asks for it, and trusting the diff object cache by default. Sync conflict previews are cached in jj-backend now too.
Import and push paths got the same treatment. Git import uploads objects as compressed VXPK packs, with tree and commit uploads pipelined and mapping writes running concurrently. The GitHub projection path stopped hydrating objects it already had in memory, and batches the rest. Pushing no longer repacks the whole repository: the pack store uses a geometric layout, so a push touches work proportional to what it added.
Behind that, the pack store ran its first production compaction campaign, reclaiming 25.77 GiB — with the destructive pass bound to the exact set of packs a human reviewed, and structural retirement running unattended after that.
Hosting: drain, size, and store
Hosted apps got the controls that separate a demo from a platform.
- Graceful drain. Services render a real
ShutdownDelayandKillTimeout, so a deploy stops taking new traffic before it stops answering the requests it has. - A real CPU ceiling on app services, and memory oversubscription — reserve small, burst large — which is what made it possible to run more replicas on the same fleet.
- Local NVMe volumes as a customer storage provider, for workloads that want fast local disk rather than a network round trip.
- Sizing from the CLI and MCP, so an agent can size a service and see why the number matters.
- Rollback that works for single-target services — and, when it cannot roll back, says so instead of failing silently.
We also stopped a failure in one region wedging a service everywhere, gave DNS a real second resolver, and stopped running jj-backend and its volumes as root.
GitHub sync, hardened
The GitHub integration spent the week getting less clever and more correct. Abandoning a change now closes its pull request and applies branch retention. The sync-enabled flag became three-state, so a background sync can no longer re-enable a configuration someone deliberately disabled. Imported approvals are gated on reviewed content identity, so an approval cannot survive a change to what it approved. Projections retry outside stream transactions, and Vex targets map correctly onto GitHub branches.
Organizations can now disconnect a GitHub App installation from the UI. And GitHub import streams its progress live during onboarding, instead of leaving you watching a spinner with no sense of scale.
Agents
Roder — the agent that works inside your repository — picked up model and reasoning-effort pickers, web search, and an allowlist for hosted runs. Agent sandboxes now receive refreshable user API tokens, so a long-running session stops dying halfway through on an expired credential.
The agent panel became a floating surface you can open from the launcher, minimize with a proper dock-timing animation, and blow up to full screen. System-injected messages are hidden from the transcript, so what you read is the conversation, not the plumbing. Agent transcripts also isolate each session when a seal links checkpoints, which fixes transcripts bleeding between concurrent runs.
Everything else
The interface moved to Hugeicons throughout and dropped its default type size to 13px, which sounds small and reads as a noticeably denser, calmer product. The sidebar gained a utility row and live “glances” — agent and deployment activity where you can see it without navigating.
On the infrastructure side: CLI downloads now come from dl.vex.sc, container pull
tokens are minted on the node so a pull never depends on the edge, the registry
resolves public slug namespaces to canonical storage paths, and the edge learned
per-rule request timeouts, response-header overrides, and repository-keyed affinity.
Every node runs its own DNS resolver, so losing one cannot partition a region.
And a handful of correctness fixes worth calling out on their own: Vex now refuses to
advance a shared ref to a conflicted commit, refuses a submit whose objects were never
finalized, and repoints a stale trunk() alias when the server retires that bookmark
instead of blocking every pull.
That is the week. The Reflog runs weekly — same day, same shape. If something here changed how your week went, or should have and did not, tell us.