Decision 0044
A repository is read as the person, and nothing in it is written
Architecture decision record 0044: A repository is read as the person, and nothing in it is written
Context
The plan gives the agent git as typed tools — status, diff, log, show, branch, worktree, commit —
with the destructive operations classified irreversible. The first of those the machine needs is
the reading half: “what changed in my project” and “what did I commit yesterday” are questions a
person asks their machine, and a machine that can read their files but not their repository is
answering with cat.
A person’s files are read by their executor, as them (ADR-0035’s session executor), and that
executor runs with the home read-only: the process that can read everything a person owns can
change none of it. That decides what git can be here. git status refreshes the index when it
can, which is a write; and a commit is a write by definition.
Prior decisions
ADR-0018 makes native tools typed and parsers of untrusted data sandboxed; ADR-0010 says content from the world is never an instruction; ADR-0029 lists git’s destructive operations among the irreversible.
Decision
Five reads, one method, no writes, and git told to take no locks.
executor.git takes a path in the person’s home and one of a closed set of operations — status,
diff (working tree or staged), log (twenty commits unless asked, a hundred at most), show of one
revision, branches — and runs git as the person in the repository the path is in, found by
walking up no further than the home. Every operation is a fixed argument vector; a revision is
checked before it becomes one, so nothing git would read as an option arrives as one. Git is run
with GIT_OPTIONAL_LOCKS=0, so a status that would have refreshed the index does not, and with
prompts off, so nothing waits for a person. What comes back is untrusted, whole: commit messages
and diffs are the world’s.
The daemon’s git.read tool goes to the person’s executor for a repository in a home, and reads
one anywhere else — the genome — as itself. The rules model turns “git status of ~/project”,
“what changed in ~/project”, “git log of ~/project” and “branches of ~/project” into reads, by
the path as typed.
Writing — a commit, a branch, a worktree — is not in this record. It needs an executor that may write, under a policy that says what, and the classification of the destructive operations the plan names; that is a decision of its own, and an agent with no tool for it cannot do it.
Consequences
- The session executor’s unit gains git on its path, and nothing else changes in it: the home stays read-only, and the lifecycle test checks that a repository read is a repository unchanged.
git.readon a path that is in no repository is refused rather than run, and a revision that is not one never reaches git.- The task the plan names — typed git tools with destructive operations classified — is half done and says so: reads are here; writes wait for a writable executor.
Alternatives rejected
- Running git as the daemon on a person’s repository. The daemon’s uid sees no home, on purpose.
- A tool per operation. One tool with a closed set of operations is what a small model reaches for reliably, and the set is what keeps it closed.
- Letting
git statusrefresh the index. It is a write, and the executor does not write.