Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions templates/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
# <name>@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
Expand Down Expand Up @@ -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
Expand Down
64 changes: 64 additions & 0 deletions test/template.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
}