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§
- Into
Live - What a projection is built over: something that can hand out the current context.