Expand description
The resident commit service: protocol, server, client, journal, provisioning and logging. What: The resident commit service: protocol, server, client, journal, provisioning and logging. Why: The service is a second program with its own life.
In TS you’d write (pseudocode):
// service/index.ts: one `export * from './file'` per module below.Modules§
- client_
journal - One entry from the wrapper a person ran to the journal, sent without waiting.
What:
One entry from the wrapper a person ran to the journal, as a datagram on the native socket, sent without waiting and
never failing the command.
Why:
The wrapper is no service: its standard error is the person’s terminal or an agent’s capture, which is gone when the
command ends. A decision the person was told about once (an add that took the slow way and why) is then recoverable
afterwards only from the journal (
journalctl --identifier=cli-git). The socket is unconnected and non-blocking, so a journal that is absent, restarting or full costs the command nothing; the entry is then only in the debug log. - journald_
native - The native journal protocol and the standard error fallback the service’s entries go through.
What:
The service’s way into the journal: the native journal protocol over a datagram socket when the service’s standard
error is the journal, and a plain line on standard error otherwise.
Why:
A line on a journal stream socket is stamped by journald when it processes it, so under load the times are seconds
late and out of order with the service manager’s own lines. A datagram on the native socket carries the kernel’s
time from the moment it was sent (measured in
doc/handover/cli-git-logging.md), and the entry can carry a priority and fields a reader filters on (journalctl CLI_GIT_REQUEST_ID=<id>). The protocol is at https://systemd.io/JOURNAL_NATIVE_PROTOCOL/, and$JOURNAL_STREAMis described insystemd.exec(5). No lock is taken on the native path: the socket is unconnected and non-blocking, onesendtoper entry, and an entry that cannot be sent (journald restarting, a full queue) goes to standard error instead, so a commit never waits for the journal. A value is carried as written, one with a newline in the protocol’s length-prefixed form. On the fallback a line that finds no room is dropped and counted, never waited for, and a line the stream took only in part is finished before the next one starts. - service_
busy - What the one landing thread is doing, shared with the threads that talk to waiting clients.
What:
What the commit service’s one landing thread is doing, shared with the threads that talk to waiting clients: the write it is
inside when that write is one the wrapper never ends, the requests of the group it lands, and the withdrawals of requests
queued behind it that can be answered while it is stuck.
Why:
The owner, 2026-11-04: “Never cut a branch write (Recommended)” (the option read: “Con: a truly hung write keeps the service
busy, and later commits fail at their own 20-minute ceiling with an error naming it”). With no ceiling on the branch write, a
write that never returns holds the one landing thread, which also answers every withdrawal between two landings
(
service_server.rs); a client queued behind it would reach its answer ceiling, ask to be withdrawn, hear nothing for the withdrawal ceiling and end with an error that names nothing. This record lets the thread that reads each connection (a) tell the client what the service is stuck in, and (b) answer the withdrawal of a request that has not begun to land. The record holds no lock (the owner: “Use as few locks as possible.”; the lock inventory guard,source_guard/service_lock_inventory_tests.rs): one owner per item and exclusive creation, in a directory of the journal. The landing thread alone writes the filestep(a rename of a complete file) and creates<id>.flightfor each request of the group it is about to land; a connection thread alone creates<id>.withdrawn. Each side creates its own marker first and then looks for the other’s. If both are there, the landing drops the request and the connection thread leaves the withdrawal to the landing thread, which answers “withdrawn” for a dropped request, so the answer agrees with what happened; if the connection thread’s marker is first and complete the request is dropped when its group begins; if the landing’s marker is first the connection thread removes its own and the withdrawal waits for the landing. A request is withdrawn or landed, never both. - service_
client - The wrapper’s side of a commit through the commit service.
What:
The wrapper’s side of a commit through the resident service: connect, wait honestly while the operating system
starts the service, send the request, show the queue position, survive a service that dies mid-request, and turn
every failure into the plain text of
service_messages.rs. Why: A failure the system can resolve is resolved here without the user: a service that is starting is waited for, one that crashed is reconnected to with the same request identity, one that was started from another release of the wrapper (older or newer, found by the release its welcome names) is asked to retire and is reached again once the manager has started the current one. Only what cannot be resolved ends the command. - service_
fault - Test-only fault points of the commit service.
What:
Test-only fault points of the resident service:
CLI_GIT_TEST_ONLY_SERVICE_FAULT=<point>ends the service withSIGKILLthe moment it reaches the named point of a batch. Why: The kill tests need a deterministic instant, as the transaction phase markers give the wrapper (transaction_phase.rs). The marker is compiled into the release executable, and only the explicit variable arms it. - service_
instance - Which commit service instance belongs to a worktree: its name, socket and unit instances.
What:
Which commit service instance belongs to a worktree: a name made from the worktree’s own Git directory, and the
socket, the unit instances and the journal directory that name gives.
Why:
One service instance runs per worktree, the way Bazel runs one server per workspace, so a wait in one worktree (a foreign
holder of its index lock, a slow push) never delays another’s commits. A worktree’s own Git directory (the main
worktree’s
.git, or.git/worktrees/<id>of a linked one) holds itsHEAD, its index and its work records, and Git allows one branch to be checked out in one worktree only, so the instances never write the same branch (doc/planning/cli-git-resident-service.md, “One service per worktree”). The name is a hash because an escaped path does not fit a unit name for an ordinary home directory, and the hash is the same in the wrapper and in the service, which are built together. A collision of two worktrees would only make one instance serve both, never land anywhere wrong. - service_
journal - The commit service’s journal and its restart decisions.
What:
The resident service’s journal: one file per batch of commits, with the one durable write the batch needs, and the
decision a restart makes about a batch it finds open.
Why:
The branch moves only after the intent is on disk, so after any kill the journal and the branch say whether the
batch landed; a restart finishes the one step that may be missing (the index update) or drops the intent, and a
request that landed is remembered by its identity so a retry is answered, not landed twice
(
doc/planning/cli-git-resident-service.md, “Durability and crash recovery”). - service_
protocol - Messages between the wrapper and the resident commit service.
What:
The messages between the wrapper and the resident commit service, one JSON object per line.
Why:
A client sends a prepared change with a request identity and waits; the service answers with a landing, a queue
position, a request to prepare again, or a failure from a closed list. The line form needs no framing library and
every field is checked on read, so a damaged or hostile line is refused rather than guessed
(
doc/planning/cli-git-resident-service.md, “Protocol and request identity”). - service_
provision - The wrapper’s own provisioning of the commit service’s socket activation.
What:
The wrapper makes socket activation for a worktree’s commit service exist the first time a commit needs it: it writes
the two template unit files into the user’s runtime unit directory, asks the user manager (through the session bus) to
reload and to start the worktree’s socket instance, and returns. The manager, not the wrapper, starts the service when
the first connection arrives.
Why:
There is no install step to remember (
doc/planning/cli-git-resident-service.md, “Provisioning”): like Bazel’s client, the wrapper sets up what it needs and tells nobody. One service runs per worktree, so the units are templates (cli-git@.socket,cli-git@.service) and each worktree’s instance is named by a hash of its own Git directory (service_instance.rs). The units live under$XDG_RUNTIME_DIR/systemd/user, which disappears at logout, so nothing is left behind in the home directory and the next first use provisions again. Every step is idempotent (a file is written, by temporary file and rename, only when its content differs), so several commits racing to provision agree without any lock. Every commit that goes to the service compares the two installed templates with what this wrapper would write (two small reads) before it touches the bus, so an upgrade or a downgrade, which moves the service program the template names, is repaired by the first commit that follows it: templates rewritten, manager reloaded, failed state cleared, the worktree’s socket started. The single service of the earlier releases (cli-git.socketandcli-git.service, one socket for every worktree) is asked to retire and stopped, and its units and socket file are removed, by the first commit of the new release; its open batches are finished by the instance of the worktree they belong to, and the work it had claimed is claimed again by the next drain. Units this wrapper’s earliest releases wrote into~/.config/systemd/useroutrank the runtime ones in the unit search path, so they are recognized by their content and removed; a unit of the same name that something else wrote is never touched, and the commit stops with a message naming the file. - service_
reply - The connection of one waiting client, answered by the landing thread and kept alive by a beat from the reading thread.
What:
The service’s side of one waiting client: the queue of messages the landing thread answers with, and the loop that writes
them to the client’s connection together with a heartbeat while nothing else is to say.
Why:
A client waits for its answer with no clock of its own (
service_client.rs), so the only thing that ends its wait is silence. The thread that read the request has nothing else to do while the request is queued or landing, so it writes the heartbeats, and a long batch, a long queue or a slow disk in front of the landing never reads as a dead service. The landing thread hands its messages to that same thread through a channel instead of writing the connection itself, so the connection has one writer, a line is written whole, and no lock guards it. - service_
review - The service’s handler for a review work item, bounded by a call deadline and a total budget.
What:
The service’s handler for a
reviewwork item: read the landed commit, ask the decision gateway, and append the entry to the repository’s live shadow log, every step ending on no progress and none on the clock. Why: This replaces the repository’s post-commit hook that ranmise run shadow-review(doc/runbook/standards-review-gate.md). The review shares the service’s work thread with pushes and upkeep, so every step ends by itself: the gateway call on the idle timeouts of its transport, the Git reads under the watch ofchild_watch.rs; the local write of the entry has no judge (pick 9 ofdoc/planning/cli-git-shipped-limits-audit.md, the coordinator’s under the owner’s delegation and open to the owner’s veto; it replaces the 20 second budget around the whole run). A review that fails leaves its item infailedas a plain record and the log shows the gateway’s failure as an entry; nothing is ever left claimed. - service_
review_ key - The standards-review credential: redacted when printed, read from the wrapper’s environment, sent in the work message.
What:
The gateway credential of the standards review as it travels from the wrapper to the service: a value that prints as
redacted, is read from the wrapper’s own environment, and is carried only in the in-memory
workmessage over the user-only socket. Why: The credential must not reach the on-disk work item, a unit file or the manager’s environment, and must not appear in a log line by accident. A type whoseDebugoutput hides the value keeps it out of every{:?}the code or a test prints; reading it takes the explicitexpose. Nothing here depends on the network client, so the wrapper’s client path may use it. - service_
server - The commit service’s loop.
What:
The resident service’s loop: accept connections, queue requests in arrival order, land what is queued as a batch,
answer each client, and exit when told to retire.
Why:
One writer lands every commit, so there is no lock to take, hold or wait for: order is the order of the queue, the
cost of the one durable write is shared by the batch, and a client that waits blocks on its socket instead of
polling (
doc/planning/cli-git-resident-service.md). The service never starts itself: the operating system hands it the listening socket (socket activation), or a development run binds one. - service_
signal - The line the commit service writes when a signal ends it.
What:
The service’s last line when a signal ends it:
the service manager stops a unit with
SIGTERM, and a terminal sendsSIGINTorSIGHUP; the handler writes one line with the signal and the uptime, then lets the signal end the process as it would have without a handler. Why: The journal of the unit with the 17-second commit had no line for the first service’s end, and the manager’s own lines about the unit cannot be relied on. A signal gives the process no chance to log from ordinary code, so the line is built and written with nothing but calls thatsignal-safety(7)lists (clock_gettime,sendto,send,poll,write,signal,raise), and the write never waits for room: a standard error that is a full pipe or stream loses the line, because a service that cannot end for want of room to say so is worse than a missing line. - service_
timing - The lines the commit service writes about its life and about the time each request took.
What:
The lines the resident commit service writes to its log (standard error,
which the service manager keeps in the journal)
about its own life and about the time each request took:
when it started,
one line for each request it answered with the
phases of the wait,
a retire request,
and its exit with the reason and the uptime.
Why:
After a slow commit the journal held
readyand nothing else, so nothing said whether the time went to the queue, to another program’s hold on the index, to the landing or to a service being replaced. These lines cost one clock read per phase edge and one line per request; they name no person (the commit’s message and identities never appear) and are not shown to the person at the terminal. - systemd_
dbus - A minimal D-Bus client for the user session bus, for the provisioning’s two calls.
What:
A minimal D-Bus client for the user session bus: authenticate as the current user, say
Hello, and call methods oforg.freedesktop.systemd1.Managerwith string arguments, each bounded by one deadline for the whole exchange. Why: The wrapper provisions socket activation itself, and may start no program to do it (systemctlwould be one). The supported surface of the user manager is its D-Bus API (org.freedesktop.systemd1(5)), reached through the session bus; the manager’s own private socket is documented as private to systemd. OnlyReload,ResetFailedUnit,StartUnit,StopUnitand the state query (GetUnit, then a property read) are needed, all with at most two string arguments, so the writer and the reader below cover exactly the types those calls and the bus’s replies use (s,o,g,u, and a variant holdings) and stop at anything else. The reader is total over arbitrary bytes (a fuzz target holds it to that). The comparison with a D-Bus crate is indoc/planning/cli-git-resident-service.md, “Provisioning”. - wait_
ceilings - The longest the wrapper waits for each thing it waits for, with the measurement beside each number.
What:
The longest the wrapper waits for each thing it waits for without a person at the keyboard, with the measurement or the
decision beside each number. A step that cannot be run twice (the branch write above all) has no ceiling at all: the
owner, 2026-11-04, “Never cut a branch write (Recommended)”.
Why:
The owner, 2026-11-03: “nothing should ever wait forever or indefinitely”. Every wait on the commit path ends at one of these,
the request is withdrawn so it never lands later, the person’s index and worktree stay as they were, and the command exits
non-zero naming the blocker (
doc/planning/cli-git-no-unbounded-waits.md). The data was read on 2026-11-03 from this repository’s commit timing log (1,329 records) and the commit service’s journal (1,335 landings).