TacidOS
DevelopersGitHub

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

Status
accepted
Date
2026-09-05

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.read on 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 status refresh the index. It is a write, and the executor does not write.