Accepted adr-001 · 2026-10-12

The Agent Replaces Nothing at the Top of a Git-Dir: the Kernel Prevents, a Guard Reconciles Drift and Reports Intrusion

Context

deny-control denied the agent write on each repository's .git/config and hooks, file by file. On 2026-10-12 two facts were measured on rigzin. First, cloudatasave/.git/config, rewritten by the owner's git (config.lock renamed over it), lost the deny: the new file inherited only the parent's `group:Devel inherited allow … add_file, delete_child …`. Second, the agent replaced the file itself: `sudo -u wuji mv` over .git/config succeeded, leaving it owned by wuji:wheel and holding `x`. The deny on a file does not survive a rename, and the agent held add_file and delete_child on every .git/ through Devel.

A config written by the agent can set core.hooksPath or core.fsmonitor, which runs the agent's code as the owner on the owner's next git command. Repairing the ACL afterwards comes after that code has run.

The agent cannot be shut out of .git/ altogether: its jj workspaces of a colocated repository write objects, refs and their own .git/worktrees/<name>/.

Decision

THE AGENT CREATES, REPLACES AND DELETES NOTHING AT THE TOP LEVEL OF A GIT-DIR. deny-control gives every .git and .internal.git under host.shared.roots two agent entries: `deny add_file,add_subdirectory,delete_child,delete,…` on the directory, not inherited, and `deny write,append,delete,… file_inherit,limit_inherit,only_inherit`, inherited by the files created directly in it and by nothing deeper. A config, HEAD or packed-refs the owner's git rewrites is born denied; objects/, refs/ and worktrees/<name>/ stay writable for the agent.

THE KERNEL PREVENTS; A GUARD RECONCILES. `isolation.nu witness` exits 1 on any gap. The isolation.guard LaunchAgent (`guard install`, host.guard) runs `guard run` every host.guard.interval seconds: it re-applies the missing entries to what the owner's own rewrites or new repositories opened, without sudo.

AN AGENT-OWNED CONTROL FILE IS AN INTRUSION, NOT DRIFT. guard run never repairs it: it reports it, notifies, and exits 1, leaving the file for the owner to inspect before running git there.

Constraints

Alternatives considered