Provision machine-level agent instructions - #11
Merged
Conversation
Claude Code and Copilot CLI each read a home-directory instructions file at the start of every session -- ~/.claude/CLAUDE.md and ~/.copilot/copilot-instructions.md. Neither was written, so every session began by rediscovering the machine's conventions, or guessing wrong about them. Writes one source, /etc/orbx/agent-instructions.md, and copies it into both paths from user-setup.sh. Not the project's AGENTS.md: that file is the user's and lives in the repo mounted from the host, so writing it would dirty the tree and eventually get committed. Machine facts only -- no package inventory. Agents discover tools by running them; what they cannot discover is a convention whose failure mode is the agent "fixing" the machine. A failed psql does not make an agent stop and ask, it makes it sudo -u postgres, set a password and rewrite pg_hba.conf, leaving the sandbox diverged for every later session. Three facts clear that bar: the Postgres role is $USER with trust auth, Postgres and Redis are running systemd units (an agent's prior for a container is that there is no systemd), and there is no host filesystem. The mise shims and /etc/sandbox-status were considered and cut -- `ruby -v` just works, and agents only ever run after ready. The fourth is git. #3 already turned the credential-prompt hang into a fast failure, which an instruction file cannot do: by the time git blocks on "Username for 'https://github.com':" the agent has issued the command and has no turn left in which to read advice. What the guard cannot do is control how the error is read. "terminal prompts disabled" names its own knob, so it reads as an obstacle with a workaround rather than as policy, and the workarounds get worse as they go: GIT_TERMINAL_PROMPT=1 restores the hang, a credential helper writes plaintext creds, and a token in a remote URL lands in .git/config -- which is bind-mounted, so it is a credential written to the host's disk that outlives the sandbox. The file prohibits all three by name and points at `gh auth login` as an escalation: that flow is interactive, so the agent must ask rather than attempt it. Authentication is not blocked, only interactive authentication -- a credential helper works fine under the guard. The file defers to the repository's own instructions and carries a maintainers' note holding the line at machine facts. Copilot CLI ranks personal instructions above the repo's AGENTS.md, so a style or workflow opinion added here would silently outrank the user's project instructions in every sandbox, with nothing to indicate why. Both lookup paths are a contract with each vendor, not a convention we picked. If either moves upstream this fails silently -- provisioning still succeeds and agents go back to guessing -- so the tests pin both paths and the seed logs a warning when the source is missing. Guarded on the file rather than the directory: the Claude Code and Copilot installers run earlier in user-setup.sh and may already have created ~/.claude and ~/.copilot, where a dir check would skip the copy on a fresh machine. A file check also preserves a user's own file. Closes #6
defkode
force-pushed
the
agent-instructions
branch
from
August 30, 2026 19:26
20a17e4 to
1cea78d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #6. Implements the scoped-down version agreed in this comment.
What
Both agents read a home-directory instructions file at the start of every session —
~/.claude/CLAUDE.md(Claude Code) and~/.copilot/copilot-instructions.md(Copilot CLI). Neither was written, so every session began by rediscovering the machine's conventions or guessing wrong about them.This adds one source,
/etc/orbx/agent-instructions.md, and seeds it into both paths fromuser-setup.sh. The project'sAGENTS.mdis never touched — it lives in the repo mounted from the host and belongs to the user.What's in the file
Machine facts only, no package inventory. Agents discover tools by running them; what they can't discover is a convention whose failure mode is the agent fixing the machine. A failed
psqldoesn't make an agent stop and ask — it makes itsudo -u postgres, set a password and rewritepg_hba.conf, leaving the sandbox diverged for every later session.Four facts clear that bar:
$USERwith trust auth on local + loopbackConsidered and cut: mise shims on PATH (
ruby -vjust works) and/etc/sandbox-status(agents only run afterready— that's MOTD material for a human).Why the git entry is worded as a prohibition
#3 already turned the credential-prompt hang into a fast failure, and an instruction file can't do that job — by the time git blocks on
Username for 'https://github.com':the agent has issued the command and has no turn left in which to read advice.What the guard can't do is control how the error is read.
terminal prompts disablednames its own knob, so it reads as an obstacle with a workaround rather than as policy. The workarounds get worse as they go:GIT_TERMINAL_PROMPT=1— restores the exact hang Harden default template against non-interactive hangs #3 removed~/.git-credentials, VM-local.git/configis bind-mounted, so that writes a credential to the host's disk, where it outlives the sandboxAll three are prohibited by name, with
gh auth logingiven as an escalation: that flow is interactive, so the agent must ask rather than attempt it. The file also notes that only interactive auth is blocked — a credential helper works fine under the guard.Machine facts only, as a standing rule
The file carries a maintainers' note stating the bar, because Copilot CLI ranks personal instructions above the repo's
AGENTS.md. Machine facts are safe there since no project competes with them; a style or workflow opinion added later would silently outrank the user's own project instructions in every sandbox, with nothing to indicate why. The file also defers to the repository's instructions explicitly.Notes for review
The vendor paths are a contract, and they fail silently. If either agent moves its lookup path upstream, provisioning still succeeds and agents quietly go back to guessing — there is no error to catch. Both paths are pinned by tests, and the seed logs a warning if the source is missing.
Guarded on the file, not the directory. The Claude Code and Copilot installers run earlier in
user-setup.shand may already have created~/.claudeand~/.copilot; a dir check would then skip the copy on a fresh machine. A file check also preserves a user's own file.Verification
bats test/— 79/79 pass, 6 newshellcheck bin/orbx test/helpers/orb-stub script/release— cleanwrite_fileshas the expected entriesbash -non the extractedprovision.shand on theUSEREOFheredoc as it will land in the VM, plus shellcheck on the extracted guest script — the line continuations survive the nesting$HOME: seeds when the installers pre-created the dirs, is idempotent, and does not clobber a user's ownCLAUDE.mdNot yet verified on a live machine — worth one
orbx upon a fresh sandbox to confirm both files land populated andgit statusis clean in the mounted repo (the issue's acceptance criteria).