Skip to main content
Mythos

This buildbook converges a Git repository to the state the 📝The Git Agent Protocol — Agent Runbook needs to operate against it — a 📝Runbook for the recurring session and release cycle: branch protection configured on staging and main, the worktree shell functions installed in your shell, and the repo's tracking files in place. Running it twice against an already-converged repo makes no further changes.

Prerequisites

  • A GitHub repository with admin access (to configure branch protection).
  • gh CLI installed and authenticated (gh auth login), with a token scoped for repo administration, PR merge, and git-ref deletion.
  • A CI pipeline that can run at least one required check named test (a security check is recommended but optional — adjust the contexts arrays below to match whatever checks you actually run).
  • A deploy platform with a manual-approval gate for production (Railway and Render both support this natively; on platforms that don't, a required-reviewer rule on the release PR is the minimum substitute).
  • bash or zsh, to install the worktree functions below.

Quickstart

Every step below checks current state before acting — safe to run against a repo that's partially or fully set up already.

  1. Check branch protection on staging and main (gh api repos/<org>/<repo>/branches/<branch>/protection); if the response already matches the target settings in Reference below, skip — otherwise apply the payloads in Exact Commands & Payloads. This reconciles: branch protection is buildbook-owned configuration, so drift gets corrected on re-run.
  2. Check whether the shell functions file already exists at your chosen path (e.g. ~/.local/bin/agent-functions.zsh) with current content; if so, skip — otherwise save the functions from Reference Implementation there and ensure your shell profile (~/.zshrc or ~/.bashrc) sources it. This reconciles: the tooling is buildbook-owned.
  3. Check whether .agent-config already exists in the repo root; if present, leave it untouched — otherwise create it from the schema in Reference Implementation. This is create-if-absent, never overwrite: .agent-config is adopter-owned the moment it exists, holding this repo's own values.
  4. Check whether CONTEXT.md and ROADMAP.md already exist in the repo root; if present, leave them untouched — otherwise create minimal starting versions (CONTEXT.md: a one-paragraph architecture/current-state stub; ROADMAP.md: empty Backlog and Working sections). Also create-if-absent — these are seeds the adopter cultivates afterward, not files this buildbook keeps in sync.

Reference

Branch Protection Target State

Strict is on for staging because multiple agents merge there concurrently; strict is off for main because staging→main is always sequential. Full reasoning lives in the Runbook's Reference section — this buildbook only needs the target state to converge toward.

Reference Implementation

Shell functions

Save as its own file and source it from your shell profile. These are the commands the Runbook's procedures call.

_agent_repo_root() {
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 1
git worktree list 2>/dev/null | head -1 | awk '{print $1}'
}
_agent_load_config() {
local root config
root=$(_agent_repo_root) || { echo "not inside a git repo" >&2; return 1; }
config="$root/.agent-config"
[ -f "$config" ] || { echo "no .agent-config found at $config — run this buildbook's Quickstart step 3" >&2; return 1; }
source "$config"
}
_agent_detect_slug() {
local branch
branch=$(git branch --show-current 2>/dev/null) || return 1
case "$branch" in
feature/*) echo "${branch#feature/}" ;;
*) return 1 ;;
esac
}
new-agent-worktree() {
local slug="${1:-}"
[ -z "$slug" ] && { echo "Usage: new-agent-worktree <slug>"; return 1; }
local repo_root repo_name
repo_root=$(_agent_repo_root) || return 1
repo_name=$(basename "$repo_root")
cd "$repo_root" || return 1
_agent_load_config || return 1
local base_branch="${BASE_BRANCH:-staging}"
local worktree_path="$HOME/repos/${repo_name}-${slug}"
local branch="feature/${slug}"
[ -d "$worktree_path" ] && { echo "worktree path already exists: $worktree_path"; return 1; }
local current
current=$(git branch --show-current)
[ "$current" != "$base_branch" ] && { git checkout "$base_branch" || return 1; }
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "command center has uncommitted changes:"; git status --short; return 1
fi
git fetch origin || return 1
git pull origin "$base_branch" || return 1
git worktree add "$worktree_path" -b "$branch" "$base_branch" || return 1
cd "$worktree_path" || return 1
echo "worktree ready at $worktree_path on $branch — launch your agent here"
}
kickoff-prompt() {
local task_id="${1:-}"
local repo_root slug
repo_root=$(_agent_repo_root) || return 1
slug=$(_agent_detect_slug) || { echo "not on a feature/* branch — run from inside a worktree"; return 1; }
_agent_load_config || return 1
[ -z "${OPERATING_CONTEXT_ID:-}" ] && { echo "OPERATING_CONTEXT_ID is not set in .agent-config"; return 1; }
if [ -z "$task_id" ]; then
[ -z "${DEFAULT_TASK_ID:-}" ] && { echo "no task id passed and DEFAULT_TASK_ID is not set"; return 1; }
task_id="$DEFAULT_TASK_ID"
fi
local worktree_path pr_base test_cmd
worktree_path="$PWD"
pr_base="${PR_BASE:-staging}"
test_cmd="${TEST_CMD:-npm test}"
local journal_step=""
[ -n "${JOURNAL_TARGET:-}" ] && journal_step=$'\nAt close-out, log one dense line to '"${JOURNAL_TARGET}"': status, what changed, the PR link.'
local prompt
prompt=$(cat <<PROMPT_EOF
This is a supervised session — a human is watching. Before any code changes, read, in this order: 1) ${OPERATING_CONTEXT_ID} — the operating context for this repo. 2) The Git Agent Protocol Runbook's own memo — the Git rules (non-negotiable). 3) ${task_id} — the task for this session. Work the items marked in-progress for this session, in the order they appear, as commits on one branch, shipped in one PR. After reading, summarize: the branch-protection settings, the Forbidden Operations, and what you plan to do. Wait for confirmation before any code changes. You are already in the worktree at ${worktree_path} on branch feature/${slug} — do not check out any other branch. When work is complete: 1) run \`${test_cmd}\` and confirm it passes. 2) open a PR targeting ${pr_base} with \`gh pr create --base ${pr_base}\`. 3) merge it via the API command in the Runbook. 4) remove the covered item from the task tracker entirely (harvest any durable learning to the operating context first) — there is no Completed section.${journal_step} If you hit an unexpected error, stop and ask rather than guessing.
PROMPT_EOF
)
if command -v pbcopy >/dev/null 2>&1; then
printf "%s" "$prompt" | pbcopy
echo "copied to clipboard — paste into your agent"
fi
echo "$prompt"
}
ship() {
local repo_root
repo_root=$(_agent_repo_root) || return 1
_agent_load_config || return 1
local release_head="${RELEASE_HEAD:-staging}"
local release_base="${RELEASE_BASE:-main}"
local gh_repo="${GITHUB_REPO:-}"
[ -z "$gh_repo" ] && gh_repo=$(git -C "$repo_root" remote get-url origin 2>/dev/null | sed -E 's|.*github\.com[:/]([^/]+/[^/.]+)(\.git)?$|\1|')
[ -z "$gh_repo" ] && { echo "could not determine GitHub repo — set GITHUB_REPO in .agent-config"; return 1; }
cd "$repo_root" || return 1
git fetch origin "$release_head" "$release_base" >/dev/null 2>&1 || return 1
local pr_numbers
pr_numbers=$(gh api "repos/${gh_repo}/compare/${release_base}...${release_head}" --jq '.commits[].commit.message' 2>/dev/null | grep -oE 'Merge pull request #[0-9]+' | grep -oE '[0-9]+' | sort -u)
[ -z "$pr_numbers" ] && { echo "no merged PRs between ${release_base} and ${release_head} — nothing to ship"; return 0; }
local pr_lines="" pr_count=0 line
while IFS= read -r pr; do
line=$(gh pr view "$pr" --repo "$gh_repo" --json url,title --jq '"- " + .url + " — " + .title' 2>/dev/null)
[ -n "$line" ] && { pr_lines="${pr_lines}${line}"$'\n'; pr_count=$((pr_count + 1)); }
done <<< "$pr_numbers"
local today title body
today=$(date +%Y-%m-%d)
title="release: ${pr_count} change(s) — ${today}"
body="## Release ${today}"$'\n'"${pr_count} change(s) merging from \`${release_head}\` to \`${release_base}\`:"$'\n'"${pr_lines}"
gh pr create --repo "$gh_repo" --base "$release_base" --head "$release_head" --title "$title" --body "$body"
}
cleanup-worktree() {
local arg_slug="${1:-}"
local repo_root current_dir slug
repo_root=$(_agent_repo_root) || return 1
current_dir="$PWD"
_agent_load_config || return 1
if [ -n "$arg_slug" ]; then slug="${arg_slug#feature/}"; else slug=$(_agent_detect_slug) || { echo "no slug provided and not on a feature/* branch"; return 1; }; fi
local repo_name worktree_path branch base_branch
repo_name=$(basename "$repo_root")
worktree_path="$HOME/repos/${repo_name}-${slug}"
branch="feature/${slug}"
base_branch="${BASE_BRANCH:-staging}"
[[ "$current_dir" == "$worktree_path"* ]] && cd "$repo_root"
cd "$repo_root" || return 1
if [ -d "$worktree_path" ]; then
if ! git -C "$worktree_path" diff --quiet || ! git -C "$worktree_path" diff --cached --quiet; then
echo "worktree has uncommitted changes — resolve first, don't force"; git -C "$worktree_path" status --short; return 1
fi
git worktree remove "$worktree_path" || return 1
fi
git show-ref --verify --quiet "refs/heads/$branch" && git branch -D "$branch"
git fetch origin
git pull origin "$base_branch"
echo "cleanup complete"
}

.agent-config schema

Create-if-absent — adopter-owned once it exists.

OPERATING_CONTEXT_ID="link or id for CONTEXT.md's canonical location"
DEFAULT_TASK_ID="link or id for ROADMAP.md's canonical location"
PUBLIC_CHANGELOG_ID="public change-log target (omit if this repo publishes none)"
BASE_BRANCH="staging"
PR_BASE="staging"
RELEASE_HEAD="staging"
RELEASE_BASE="main"
GITHUB_REPO="org/repo-name"
TEST_CMD="npm test"
JOURNAL_TARGET="where the close-out log entry goes (omit for no journal step)"

Exact Commands & Payloads

Set branch protection on staging (strict checks on, no required reviews):

gh api -X PUT repos/<org>/<repo>/branches/staging/protection -H "Accept: application/vnd.github+json" --input - <<'JSON'
{"required_status_checks": {"strict": true, "contexts": ["test", "security"]}, "enforce_admins": true, "required_pull_request_reviews": null, "restrictions": null, "allow_force_pushes": false, "allow_deletions": false}
JSON

Set branch protection on main (strict checks off, 1 required review):

gh api -X PUT repos/<org>/<repo>/branches/main/protection -H "Accept: application/vnd.github+json" --input - <<'JSON'
{"required_status_checks": {"strict": false, "contexts": ["test", "security"]}, "enforce_admins": true, "required_pull_request_reviews": {"required_approving_review_count": 1}, "restrictions": null, "allow_force_pushes": false, "allow_deletions": false}
JSON

Check current protection state before applying (idempotency check for Quickstart step 1):

gh api repos/<org>/<repo>/branches/staging/protection
gh api repos/<org>/<repo>/branches/main/protection

Troubleshooting

  • gh api protection call returns 404 → the branch doesn't exist yet, or the token lacks admin scope on the repo. Create the branch first; re-auth gh with a token that has repo admin rights.
  • Protection check (idempotency step) returns settings that partially match → apply the full payload anyway; the API is a full replace, so a partial-match state always converges to the target on the next PUT.
  • Shell functions not found after sourcing → confirm the file path is actually sourced from your active shell profile (~/.zshrc for zsh, ~/.bashrc for bash) and that you've opened a new shell or run source ~/.zshrc to reload it.
  • .agent-config created but kickoff-prompt still errors on missing values → the file exists but a required key (OPERATING_CONTEXT_ID or DEFAULT_TASK_ID) is empty; fill it in — this buildbook creates the file, it doesn't populate repo-specific values for you.

Contexts

Created with 💜 by One Inc | Copyright 2026