Skip to content

Entering the repository

entering the repository: what converges on its own and what is asked for, the three commands of the shared server, what each standing means, what is deliberately not automatic and why, and what to run when it does not come up

Rendered from docs/ENTRY.md — the same Markdown GitHub shows.

What happens when a person or an agent enters a Majordomus-enabled repository, what converges on its own, what is deliberately left to be asked for, and what to do when something does not come up. docs/ENTRY_AUDIT.md is the forensic finding this document came out of; ADR 0035 decided the agent's door and ADR 0043 the shell's; this is the operator's page.

The short version: nothing here is a command anybody has to remember. Entering the directory is enough, by either door.

The sequence

cd <repository>          direnv evaluates .envrc, which makes one call — `majordomus env
                         enter` — and evaluates what it prints: the variables a shell here
                         benefits from on stdout, the banner on stderr, the workflow bridge
                         refreshed when a declaration behind it moved, and the repository's
                         shared server ensured when nothing is serving this checkout. The
                         file itself reads nothing about the repository, builds nothing and
                         starts nothing; the executable does all of it. Nothing is waited
                         for: a server this entry starts becomes ready beside the shell, and
                         `watch_file` over the lease brings its address in when it lands.

an agent starts          the provider's start event opens the episode, makes sure the
                         repository's shared server is serving this checkout, and writes a
                         briefing the provider adds to the context it is about to build:
                         the episode, the task, what blocks acceptance, the last handover's
                         next action, and one line naming where the server stands.

an MCP client attaches   the client configuration at the root starts the launcher, which
                         never builds (ADR 0224): it runs the tree's executable, or the
                         installed release of its generation, or exits 12 naming `just
                         build`; the executable elects: the first process
                         serves, every later one bridges to it. This has always converged.

the worker announces     `majordomus_announce` puts one line of intent and the paths it
                         expects to touch on the board every other client can read.

Everything above happens without a command being typed, except the last, which is a request the bootstrap makes of the worker and the one a bridge repeats on its behalf when its server changes underneath it.

The three commands

majordomus serve status [--checkouts this|repository] [--format json]
                                            where this checkout's server stands, and — unless
                                            `--checkouts this` narrows it — every server of
                                            this repository
majordomus serve ensure [--idle S] [--wait S] [--port P]
                                            a ready server for this checkout, started if
                                            there must be one
majordomus serve stop [--wait S]            end the server this checkout's lease names

serve ensure is what the start event runs, and majordomus env enter runs the same function — one convergence, three surfaces, so that an agent and a person cannot reach two different answers about whether a server is already there. It reads the lease and probes the server it names, exactly as the election does, and converges: a ready server is reported and nothing is started; one that is still binding is waited for; an absent or stale lease gets a server started as a process of its own, and the call waits until it answers. Run twice, it starts nothing the second time. Run by three shells at once, the election lets one bind and the others attach. The whole call is bounded by --wait, and a server that did not become ready in time is reported with the standing it reached and a non-zero exit.

A server ensure starts has no client of its own, so it ends when no peer has been attached for --idle seconds. That is how ADR 0003's line — there is no process without a client — is kept true in time rather than at every instant: an agent's entry is owed a server before its first attach, and a checkout nobody works in does not keep one.

What the standings mean

standingwhat it sayswhat to do
absentno lease: nothing serves this checkoutnothing, unless you want one: serve ensure
startinga lease without an address, young enough that its owner is still bindingwait; serve ensure waits for you
readythe server the lease names answers for this checkout, from the file on disk, at this executable's versionnothing
outdatedit answers, but from another version or from a file replaced since it startedserve stop, then serve ensure
stalethe lease names a server that does not answer, or is not a lease at allnothing: the next start takes it over and says which it was

Entering as a shell

majordomus env enter [--shell SHELL] [--no-banner] [--no-bridge] [--no-runtime] [--wait S]

What .envrc calls, and the only call it makes. The file carries a PATH_add, a watch_file and that line; everything else — whether to ensure, on what port, with what idle life, when to stay silent, when to refuse — belongs to the executable, where it is typed, benchmarked and tested.

Three things about it are contract rather than implementation:

  • It never waits. A cold entry finds that nothing answers, starts a server as a process of its own, and returns. Measured on a machine under a load average of 136, a cold entry cost 185–217 ms and a warm one 134–218 ms: the same, because starting the runtime is a fork and an exec and nothing else. The budgets are benchmark.budget.enter_ms and benchmark.budget.enter_cold_ms in the policy, and test/cases/190 measures both on every run.
  • It never fails. Every runtime outcome is at most one line on standard error and an exit of 0. A non-zero exit here makes direnv report that the whole environment failed and leaves a person with a broken shell in a repository that is fine.
  • It is silent when there is nothing to say. Entering a healthy repository prints nothing about the runtime, changes no file under .ai/local/, and starts no process. A person who enters this directory forty times a day is told nothing forty times.
  • It reports only what is proven. Under the banner it draws the compact preflight (PREFLIGHT.md): the server and its surfaces, the briefing, the rules, the recorded test runs and the deployment, each with a verdict that rests on evidence. A service is never marked answering because a connection was accepted. It is marked only when the server behind it answers as this checkout's leaseholder, at this executable's version (project.entry-reports-only-evidence).

The switches. session.ensure_server_on_start in the policy governs both doors — one question, one answer, so the two cannot drift. MAJORDOMUS_RUNTIME=off is the same refusal for one person's shell, and only that exact value means it: a typo in a profile must not silently stop a repository coming up. --no-runtime is the same thing as an argument.

Until ADR 0043 this was the other way round, and the rule read "entry by a shell reports and does not serve". What that cost was the whole point of the design: a person who opened a terminal here got a banner and no runtime — no Cockpit, no API, no peer board, so no way to see that other workers were on the same machine — and the remedy was a command to remember. The objection ADR 0003 makes is about a process outliving its reason to exist, and the idle life answers it: a server nobody attaches to ends.

What is not automatic, and why

Nothing on entry builds the executable, and nothing ensures from a stale one. A missing executable, or one older than its sources, is one line on standard error naming the recipe that builds it, and the entry still exits zero — with no runtime. Everything a person sees here — the banner, the workflow bridge, the completion, the shared server — is a projection of that one file, so they go together and the line says so. A server started from stale code would answer with a tree that is no longer there, to the Cockpit, to the API and to every other worker attached to it, none of whom can see that the process is older than the checkout it claims to serve. Both doors decide staleness with the same mj_rust_stale in lib/rust_bin.sh, so a checkout where they disagree cannot exist.

No remote network, ever. The only socket entry opens is to a loopback address a server of this checkout already published.

The briefing is still the agent's. A shell is handed an environment and a banner; the episode, the task and the last handover's next action are what a provider's start event composes, because there is no episode without a worker to have one.

When it does not converge

what you seewhat it meanswhat to run
the banner is gone, just lists nothing, the completion is silentthe executable is missing or older than its sources; every surface is a projection of itjust build
Shared server: not ensured: the executable is not built in the briefingthe same, seen from the provider's start eventjust build, then start a new session
no address in the environmentno server has published one for this checkoutmajordomus serve status, then serve ensure
the server answers, but with things the tree no longer hasthe process is older than the codemajordomus serve status says outdated; serve stop then serve ensure
a linked worktree reports no server while the primary checkout has onea server serves the checkout it started in; each has its own leaseserve ensure in that worktree; serve status lists both
direnv: error .envrc is blockeddirenv approves by path and content, and a new worktree starts unapprovedmajordomus worktree ensure <branch> carries the primary's approval over
majordomus: nothing was serving this checkout; a server is startingthe expected line on a first entry; the address arrives a moment later, when watch_file sees the leasenothing
majordomus: <reason>; a server is starting in its placethe lease named a server that no longer serves this checkout — it lost the lease, runs code no longer on disk, or is not ours — and entry replaced it; the banner shows no address until the new one publishesnothing; a process that still holds the old port is named by majordomus serve status
that same line on every cdthe server is started and then failsread the log the line names, then majordomus serve status
a second banner right after the firstdirenv re-evaluated entry because the lease moved under watch_file; a server arriving or leaving draws the two-line form, never the box againnothing
majordomus: the executable is older than the sources, and no server comes upentry refuses to start a runtime from stale codejust build; the next cd ensures one
server ◐ on the entry line while the addresses are listeda server answers, from another version or a replaced executable; the banner's service marks say ○, not ✓majordomus serve stop, then serve ensure
rules ? or enforced ? on the entry linethe rule corpus is counted from the index, which entry never builds, and no full run has cached a countmajordomus env preflight --full
the peers board is empty although others are workingthe board is one server's memory, and each checkout has its own servermajordomus serve status names every server of the repository and what each holds

Where each fact lives

Nothing above is written down twice. The lease under the checkout's local state is the one place a running server's address is true, and one typed reader parses it. The port the server asks for first is declared once in the command line's own declaration. The routes belong to the surfaces that own them, and the environment snapshot renders them. What each command does is the command graph's, and this page names commands rather than restating them.

majordomus serve status --format json    the server, its lease and every checkout's
majordomus serve status --checkouts this this checkout's server alone, without reading or
                                         probing any other checkout
majordomus env status                    the checkout: project, version control, toolchains,
                                         the layer, the workflows, the local services
majordomus context                       what the next worker needs to know now
majordomus doctor                        whether Majordomus itself is healthy and wired here

What holds it shut

project.entry-converges is the rule, and scripts/ci/entry-converges the gate that decides it: the entry file starts nothing itself, makes exactly one call, and that call is the bootstrap command with no --wait; the shell's door reads the same switch, honours MAJORDOMUS_RUNTIME and converges through the same function serve ensure does; the adapter refuses to ensure from a stale executable; neither door builds; the switch and the two entry budgets are each declared in the policy, the skeleton, the allow list and the schema together; the start shim is wired where the policy says so that doctor reconciles it; the briefing forms no opinion of its own about where the server stands; and a server nobody owns has a bounded life. That the lease itself has one reader is project.the-lease-is-read-once, decided for every file at once by scripts/ci/lease-reader-check. What no script can decide — that entry actually converges — is test/cases/108_entry_converges_on_a_server.sh for the agent's door, which drives the provider's own shim, and test/cases/190_entry_is_automatic.sh for the shell's; test/cases/191_entry_races.sh and test/cases/192_entry_across_checkouts_and_clients.sh hold the convergence under real concurrency — ten simultaneous entries, a dead pid in the lease, a live process that is not ours, an abandoned lease, a port a stranger holds, two worktrees entering at once, and ten clients on a runtime an entry started. test/cases/118_entry_converges_by_rule.sh proves the gate by planting each thing the rule forbids and watching it refused.

docs/MCP.md has the shared server's lifecycle and the election in full; docs/ENVIRONMENT.md the snapshot and the adapter; docs/CONTINUITY.md the episode, the briefing and the records; docs/WORKTREES.md the branch-to-worktree topology; docs/ENTRY_AUDIT.md what was measured before any of this was built, and what is still owed.

Searches the titles and descriptions of every page this site publishes.