diff --git a/templates/default.yaml b/templates/default.yaml index 10934f1..ec72f81 100644 --- a/templates/default.yaml +++ b/templates/default.yaml @@ -165,6 +165,69 @@ write_files: │ Postgres role = your username (superuser, trust auth, local). │ └──────────────────────────────────────────────────────────────┘ + # Machine-level agent instructions. Seeded into BOTH agents' home-directory + # instruction paths by user-setup.sh below, from this single source. + # + # Those paths are a contract with each vendor, not a convention we picked: + # Claude Code ~/.claude/CLAUDE.md + # Copilot CLI ~/.copilot/copilot-instructions.md + # If either ever moves, this fails SILENTLY -- provisioning still succeeds and + # agents simply go back to guessing. There is no error to catch. + # + # Machine facts only. See the "Maintainers" note in the file itself for why. + - path: /etc/orbx/agent-instructions.md + permissions: '0644' + content: | + # Machine notes (orbx sandbox) + + Facts about the machine you are running on, provisioned by orbx. These are + not project instructions: the repository's own AGENTS.md / CLAUDE.md still + governs how to work on the code, and wins wherever the two could disagree. + + > **Maintainers:** machine facts only -- nothing here that a project could + > reasonably want to override. No style, workflow, testing or commit + > conventions. Copilot CLI ranks personal instructions *above* the + > repository's AGENTS.md, so an opinion added here would silently outrank + > the user's own project instructions in every sandbox, with nothing to + > indicate why. orbx provisions the machine, so it describes the machine. + + ## This is an isolated VM + + There is no host filesystem. Only the project directory is shared, as a + live bind mount -- writes there land on the host immediately and survive + this machine being deleted. Everything outside it is disposable. + + ## PostgreSQL and Redis are already running + + Both are systemd units, enabled and started at provisioning. Use + `systemctl status postgresql` / `systemctl status redis-server`. Do not + hand-start them, and do not assume this container has no systemd. + + The Postgres superuser role is your Unix username, with trust auth on + local and loopback connections. No password is needed or wanted -- a bare + `psql` and `bin/rails db:create` work as-is. If a connection fails, report + the error; do not create roles or edit `pg_hba.conf`. + + ## git cannot prompt for credentials + + `GIT_TERMINAL_PROMPT=0` is set machine-wide, deliberately. Without it a + fetch or push against a private repo blocks on + `Username for 'https://github.com':` until the session is killed, which + reads as a slow network rather than as a stuck process. + + If a git operation fails on authentication, **stop and ask the human to + run `gh auth login`**. That flow is interactive, so you cannot run it. + + Do not work around it: + + - do not set `GIT_TERMINAL_PROMPT=1` -- that restores the hang + - do not add a credential helper or write `~/.git-credentials` + - do not put a token in a remote URL -- `.git/config` lives on the host's + disk, so a credential written there outlives this sandbox + + Authentication is not blocked, only *interactive* authentication: once + `gh auth login` has configured a credential helper, git works normally. + # Default Starship prompt: icon-free and low-noise. Shows user, machine as # @orb, path, and git only -- no language versions or container badge. # Seeded into each user's ~/.config on first boot (see user-setup.sh) so it @@ -391,6 +454,23 @@ write_files: cp /etc/starship.toml "$HOME/.config/starship.toml" fi + # Seed machine-level instructions into both agents' home-directory paths + # from the single source in /etc/orbx. Guarded on the FILE, not the dir: + # the Claude Code and Copilot installers above may already have created + # ~/.claude and ~/.copilot, and a dir check would then skip the copy. + # Never touches the project's AGENTS.md -- that file is the user's, and it + # lives in the repo mounted from the host. + if [ -f /etc/orbx/agent-instructions.md ]; then + mkdir -p "$HOME/.claude" "$HOME/.copilot" + [ -f "$HOME/.claude/CLAUDE.md" ] \ + || cp /etc/orbx/agent-instructions.md "$HOME/.claude/CLAUDE.md" + [ -f "$HOME/.copilot/copilot-instructions.md" ] \ + || cp /etc/orbx/agent-instructions.md "$HOME/.copilot/copilot-instructions.md" + log "Seeded machine-level agent instructions." + else + log "WARNING: /etc/orbx/agent-instructions.md missing; agents unguided" + fi + # Sensible git defaults (name/email left for you to set) git config --global init.defaultBranch main git config --global push.autoSetupRemote true diff --git a/test/template.bats b/test/template.bats index 258b0a6..0c6d310 100644 --- a/test/template.bats +++ b/test/template.bats @@ -195,3 +195,67 @@ load helpers/test_helper run grep -E "^\s+- postgresql-contrib$" "$ORBX_TEST_ROOT/templates/default.yaml" [ "$status" -ne 0 ] } + +# --- bundled default template: machine-level agent instructions (issue #6) --- +# Both agents read a home-directory instructions file at the start of every +# session. orbx seeds both from one source so agents stop rediscovering (or +# guessing wrong about) the machine's conventions. + +@test "default template ships a single agent-instructions source" { + run grep -cE "^\s+- path: /etc/orbx/agent-instructions\.md$" \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] + [ "$output" -eq 1 ] # one source; both agent files are copies of it +} + +@test "default template seeds both agents' instruction paths" { + # Vendor-fixed lookup paths: Claude Code reads ~/.claude/CLAUDE.md, Copilot + # CLI reads ~/.copilot/copilot-instructions.md. If either moves upstream this + # breaks silently, so pin both. + run grep -F '"$HOME/.claude/CLAUDE.md"' "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] + run grep -F '"$HOME/.copilot/copilot-instructions.md"' \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] +} + +@test "instruction seeding is 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. + run grep -F '[ -f "$HOME/.claude/CLAUDE.md" ]' \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] + run grep -F '[ -f "$HOME/.copilot/copilot-instructions.md" ]' \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] +} + +@test "default template never writes the project's AGENTS.md" { + # That file lives in the repo mounted from the host. Writing it would dirty + # the user's git tree and eventually get committed. + run grep -E "(^\s+- path:.*AGENTS\.md|cp .*AGENTS\.md|> *\"?\\\$HOME.*AGENTS\.md)" \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -ne 0 ] +} + +@test "agent instructions carry the facts an agent would otherwise misdiagnose" { + # Not a tool inventory -- agents discover tools by running them. These are the + # conventions whose failure mode is the agent "fixing" the machine instead. + run grep -F 'trust auth on' "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] + run grep -F 'do not assume this container has no systemd' \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] + run grep -F 'do not set `GIT_TERMINAL_PROMPT=1`' \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] +} + +@test "agent instructions state that project instructions win" { + # Copilot CLI ranks personal instructions above the repo's AGENTS.md, so this + # file must defer explicitly and stay to machine facts. + run grep -F 'wins wherever the two could disagree' \ + "$ORBX_TEST_ROOT/templates/default.yaml" + [ "$status" -eq 0 ] +}