Skip to content

Provision machine-level agent instructions - #11

Merged
defkode merged 1 commit into
mainfrom
agent-instructions
Aug 30, 2026
Merged

Provision machine-level agent instructions#11
defkode merged 1 commit into
mainfrom
agent-instructions

Conversation

@defkode

@defkode defkode commented Aug 30, 2026

Copy link
Copy Markdown
Owner

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 from user-setup.sh. The project's AGENTS.md is 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 psql doesn't 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.

Four facts clear that bar:

  • Postgres role is $USER with trust auth on local + loopback
  • Postgres and Redis are running systemd units (an agent's prior for a container is that there is no systemd)
  • No host filesystem; only the project dir is shared, as a live bind mount
  • git cannot prompt for credentials

Considered and cut: mise shims on PATH (ruby -v just works) and /etc/sandbox-status (agents only run after ready — 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 disabled names its own knob, so it reads as an obstacle with a workaround rather than as policy. The workarounds get worse as they go:

  1. GIT_TERMINAL_PROMPT=1 — restores the exact hang Harden default template against non-interactive hangs #3 removed
  2. a credential helper — plaintext creds in ~/.git-credentials, VM-local
  3. a token in the remote URL — .git/config is bind-mounted, so that writes a credential to the host's disk, where it outlives the sandbox

All three are prohibited by name, with gh auth login given 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.sh and may already have created ~/.claude and ~/.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 new
  • shellcheck bin/orbx test/helpers/orb-stub script/release — clean
  • YAML parses; write_files has the expected entries
  • bash -n on the extracted provision.sh and on the USEREOF heredoc as it will land in the VM, plus shellcheck on the extracted guest script — the line continuations survive the nesting
  • Simulated the seed against a fake $HOME: seeds when the installers pre-created the dirs, is idempotent, and does not clobber a user's own CLAUDE.md

Not yet verified on a live machine — worth one orbx up on a fresh sandbox to confirm both files land populated and git status is clean in the mounted repo (the issue's acceptance criteria).

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
defkode force-pushed the agent-instructions branch from 20a17e4 to 1cea78d Compare August 30, 2026 19:26
@defkode
defkode merged commit 85022c0 into main Aug 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provision machine-level agent instructions

1 participant