Skip to main content

Module live

Module live 

Source
Expand description

The repository as a long-lived process must see it: current, not as it was when the process started.

§The defect this exists to end

crate::app::App::load builds one Index and one Context and hands them out as Arcs. For a command that answers a question and exits, that is exactly right: the picture cannot go stale inside a process that lives for 40 ms. The shared server lives for hours, and it held the same two values for its whole life. A session would commit, ask GET /api/v1/repository for the head it had just written, and be told the head the server had read when it started — the same answer from MCP, from the Cockpit and from every capability that reads the index, with nothing in any of them saying the picture was old. Measured on origin/master at faba8fdef: head A before a commit, head A after it, a rule committed while the server ran absent from objects, and a working tree reported clean while it was dirty. Only restarting the process fixed it.

§What is watched, and why it is these files

Git announces every move of the repository by writing a small, fixed set of files: HEAD, the staging index, packed-refs, the reflog, and the in-progress markers a merge or a rebase leaves. A commit, a checkout, a reset, a merge, a rebase and a branch switch each write at least one of them. So the question “has the repository moved?” is Stamp: the size and modification time of those files, hashed — one stat per watched file, taken on the request’s own thread before it is answered, which the_stamp_is_cheap_enough_to_take_on_every_request holds to well under the millisecond that would make it matter. No thread, no interval, no watcher to leak, and nothing over the network.

The limit, named rather than hidden: an edit to a tracked file that is never staged changes no git control file, so this does not see it. Seeing it would mean a stat per declared object on every request — a number that grows with the layer, against an HTTP p50 the benchmark baselines record in the hundreds of microseconds — which is the per-request regression the performance doctrine refuses. What a long-lived session actually loses to the frozen picture is the commit it just made, and a commit always moves git. git add moves it too: the staging index is watched.

One kind of unstaged edit is seen anyway: the ones this process makes. A capability whose effect is a repository mutation — plan.transition stamping an issue — writes a tracked file and moves no git control file, and a Cockpit page read right after the move showed the status from before it. The executor counts every such call that succeeds, whichever transport or execution made it, and outlives a reload; a generation records the count it was built after, and a count that has moved is a repository that has moved.

§The obligation that comes with watching: this process must not write them

git status and git diff refresh the staging index as a side effect, writing back stat data that has gone stale or is racily close to the index’s own modification time. So a served page that asked git about the working tree moved the stamp itself, and the next request paid a whole crate::app::App::load for a repository that had not moved — an observer disturbing what it observes, and charging the following caller for it. Measured over the Cockpit page sweep against a committed fixture: repository_scans 1 → 2 → 4 across /cockpit/continuity and /cockpit/health, with .git/index’s modification time moving at exactly those two points and its size never changing. Every read this crate makes therefore goes through crate::git::read_only, which is that one rule made into a constructor; a_page_costs_no_rebuild_of_anything_canonical holds it, page by page.

§What a new generation costs, and who pays it

Rebuilding is a whole crate::app::App::load: discovery, every declared file read and validated, the registry, the Why catalogue, the product model, the web topology. It is paid once per repository move, by the one request that noticed, and never concurrently — a second request arriving mid-rebuild waits for it and is answered from the generation it produced, because it has seen the move too and the previous generation is the stale answer it was asking about. Everything a generation carries is a projection of the index and is therefore rebuilt with it. What is not a projection of the index is carried across: the peer board, the executions this process is running and the capability executor, because a reload is not a restart and an attached peer must not lose its place on the board because somebody committed.

use std::sync::Arc;
use majordomus_cli::live::Live;

// A pinned view never asks the filesystem anything: what a one-shot command,
// a benchmark and a test want, and what they cost.
let live = Live::pinned(ctx);
assert_eq!(live.generation(), 0, "a pinned view has one generation, forever");
let a = live.current();
let b = live.current();
assert!(Arc::ptr_eq(&a, &b), "and hands out the same context every time");

Structs§

Live
The repository a process reads, kept current.
Memo
A value derived from one generation of the repository, kept until that generation ends.
Stamp
A picture of the git control files: what the repository looked like when a generation was built.
View
One reading of the repository: which generation, and the context of it.

Constants§

COMMON_FILES
The git control files shared by every worktree of one repository (git rev-parse --git-common-dir): where a ref that is not this worktree’s moves.
RETRY_AFTER
How long a stamp whose load failed is answered from the previous generation before the load is tried again. A failure is not retried on every request — a layer that will not load costs a whole load each time — and it is not given up on either: the change it failed on is still the repository’s, so it is read once the interval has passed, or at once when git moves again.
SALT
The salt the stamp is taken under, so that it can never collide with another fingerprint computed over the same files for another purpose.
WORKTREE_FILES
The git control files whose size and modification time say the repository has moved.

Traits§

IntoLive
What a projection is built over: something that can hand out the current context.