mirror of
https://github.com/piabrota/mu.git
synced 2026-11-07 06:15:59 -10:00
PRs the pr-bot opens arm auto-merge; the merge invariant says a human's approval decides (#84)
* setup-repo.sh turns auto-merge on; check-repos.sh --check settings requires it A pull request merges by itself once a human approves it and the required checks pass. The ruleset is unchanged: one approval from someone other than the last pusher, and the checks. Co-Authored-By: Claude Opus 5.5 <[email protected]> * pr-open.sh opens a pull request as the bot and arms auto-merge; the merge invariant says why a human still decides Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Bruno Guilhermo de Barros Valério <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
3 files changed
+83
-2
No files matched your search
@@ -10,7 +10,10 @@ loosen these.
|
||||
|
||||
1. Humans steer. Stop and ask when work would leave an approved charter, spec or plan.
|
||||
2. Write the failing test first. A test that never failed proves nothing.
|
||||
3. Never merge a pull request, and never push to `main`. Humans merge.
|
||||
3. Never merge a pull request, and never push to `main`. Humans merge: an agent may arm auto-merge on a pull
|
||||
request it opens (`scripts/pr-open.sh`), because the ruleset still requires a human's approval, and that approval
|
||||
is the merge decision. Never approve a pull request, never use an admin bypass, and never arm auto-merge on a
|
||||
repo whose ruleset does not require an approval.
|
||||
4. Never read, print or store secrets, tokens or keys.
|
||||
5. Never weaken a guardrail: approval points, sandbox settings, policies, rulesets or security tests.
|
||||
6. Write each document in the style the docs standard assigns to its path.
|
||||
@@ -39,7 +42,8 @@ folder.
|
||||
|
||||
| Need | Script |
|
||||
| --- | --- |
|
||||
| Push, open a pull request or read GitHub as `ouroboric-pr-bot` | `scripts/as-bot.sh <repo> <cmd>`; `--read all` to read. The only way an agent reaches GitHub |
|
||||
| Push, open a pull request or read GitHub as `ouroboric-pr-bot` | `scripts/as-bot.sh <repo> <cmd>`; `--read all` to read. The only way an agent reaches GitHub; to open a pull request, `scripts/pr-open.sh` is the default |
|
||||
| Open a pull request as `ouroboric-pr-bot` and arm auto-merge on it: the default way an agent opens one. A human's approval still decides the merge; if arming fails the PR stays open and the script says why | `scripts/pr-open.sh <repo> [gh pr create args]`, e.g. `scripts/pr-open.sh gleipnir --base main --head my-branch --title "…" --body-file body.md`. Prints the PR's URL. Never merges directly, never `--admin` |
|
||||
| Any real-engine run (Podman, devcontainer, the sandbox): the engine, registry config and checks | `scripts/ygg-engine.sh check\|env\|run --folder DIR`. Never set `CONTAINER_HOST` by hand: the root socket is the wrong one |
|
||||
| The realms smoke runs (v0-H13, v0-H15) and their evidence | `scripts/ygg-smoke-realms.sh`, `scripts/ygg-smoke-loop.sh`, `scripts/ygg-evidence.sh` |
|
||||
| The grader smoke on a real engine (v0-H16), with the public sample suite | `scripts/ygg-smoke-grade.sh [--build]`. Never a hidden suite by default; `--task ID --suite-dir DIR --reference DIR [--suite-engine DIR]` points the grader at a suite store as it is (never copied or read) and grades the task's reference solution; its output holds numbers only |
|
||||
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# Open a pull request as ouroboric-pr-bot and arm GitHub auto-merge on it. The default way an agent
|
||||
# opens a pull request. Everything runs through as-bot.sh, so the token lasts one hour and reaches
|
||||
# only the named repo.
|
||||
# Arming auto-merge is not merging: the PR still waits for a human's approval and the required checks,
|
||||
# which the ruleset demands. This script never merges directly and never uses an admin bypass.
|
||||
# If arming fails, such as on a repo with auto-merge off (huldra), the PR stays open, the script says
|
||||
# why and exits 1. It never closes or deletes the PR.
|
||||
# Prints the PR's URL. Agent-run.
|
||||
# pr-open.sh <repo> [gh pr create arguments...]
|
||||
# pr-open.sh gleipnir --base main --head my-branch --title "..." --body-file body.md
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ORG="ouroboric-dev"
|
||||
die() { echo "pr-open: $*" >&2; exit 2; }
|
||||
|
||||
[ $# -ge 1 ] || die "usage: pr-open.sh <repo> [gh pr create arguments...]"
|
||||
repo="$1"; shift
|
||||
[[ "$repo" =~ ^[A-Za-z0-9._-]+$ ]] || die "bad repo: $repo"
|
||||
|
||||
out="$("$HERE/as-bot.sh" "$repo" gh pr create --repo "$ORG/$repo" "$@")" || exit $?
|
||||
url="$(printf '%s\n' "$out" | grep -Eo 'https://[^[:space:]]+/pull/[0-9]+' | tail -1 || true)"
|
||||
[ -n "$url" ] || die "gh pr create printed no pull request URL: $out"
|
||||
echo "$url"
|
||||
|
||||
if ! err="$("$HERE/as-bot.sh" "$repo" gh pr merge "$url" --auto --squash 2>&1)"; then
|
||||
{
|
||||
echo "pr-open: the PR stays open, but auto-merge is not armed: $url"
|
||||
echo "pr-open: $err"
|
||||
echo "pr-open: the repo may have auto-merge off, as huldra does; a human can merge it once approved"
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# Tests for pr-open.sh against a fake as-bot.sh. scripts/tests/pr-open.test.sh
|
||||
set -uo pipefail
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
T="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$T"' EXIT
|
||||
fail=0
|
||||
check() { local d="$1"; shift; if "$@"; then echo "ok $d"; else echo "FAIL $d"; fail=1; fi; }
|
||||
# The script runs from a copy next to a fake as-bot.sh, which logs each call and plays GitHub.
|
||||
mkdir -p "$T/s"
|
||||
cp "$HERE/../pr-open.sh" "$T/s/pr-open.sh"
|
||||
cat > "$T/s/as-bot.sh" <<'FAKE'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s\n' "$*" >> "$FAKE_LOG"
|
||||
case "$3 $4" in
|
||||
"pr create") echo "https://github.com/ouroboric-dev/gleipnir/pull/7" ;;
|
||||
"pr merge") if [ -n "${FAKE_MERGE_FAIL:-}" ]; then echo "GraphQL: Auto merge is not allowed for this repository" >&2; exit 1; fi ;;
|
||||
esac
|
||||
FAKE
|
||||
chmod +x "$T/s/pr-open.sh" "$T/s/as-bot.sh"
|
||||
export FAKE_LOG="$T/log"
|
||||
U=https://github.com/ouroboric-dev/gleipnir/pull/7
|
||||
|
||||
: > "$FAKE_LOG"
|
||||
out="$("$T/s/pr-open.sh" gleipnir --base main --head my-branch --title "A title" --body-file body.md 2>"$T/err")"; rc=$?
|
||||
check "success: exit 0" [ "$rc" = 0 ]
|
||||
check "success: the URL is printed" [ "$out" = "$U" ]
|
||||
check "success: create, then arm, in order, both as the bot for the repo" \
|
||||
[ "$(cat "$FAKE_LOG")" = "gleipnir gh pr create --repo ouroboric-dev/gleipnir --base main --head my-branch --title A title --body-file body.md
|
||||
gleipnir gh pr merge $U --auto --squash" ]
|
||||
check "no admin bypass" bash -c "! grep -e '--admin' '$FAKE_LOG'"
|
||||
check "the only merge call carries --auto" bash -c "[ \"\$(grep -c 'pr merge' '$FAKE_LOG')\" = 1 ] && grep 'pr merge' '$FAKE_LOG' | grep -q -e '--auto'"
|
||||
|
||||
: > "$FAKE_LOG"
|
||||
out="$(FAKE_MERGE_FAIL=1 "$T/s/pr-open.sh" huldra --title t 2>"$T/err")"; rc=$?
|
||||
check "arming fails: exit non-zero" [ "$rc" != 0 ]
|
||||
check "arming fails: the message says the PR stays open and why" \
|
||||
bash -c "grep -q 'stays open' '$T/err' && grep -q 'auto-merge' '$T/err' && grep -q 'Auto merge is not allowed' '$T/err'"
|
||||
check "arming fails: the URL is still printed" [ "$out" = "$U" ]
|
||||
check "arming fails: nothing closes or deletes the PR" bash -c "! grep -e 'close' -e 'delete' '$FAKE_LOG'"
|
||||
|
||||
: > "$FAKE_LOG"
|
||||
check "no repo: usage, exit 2, no call" bash -c "'$T/s/pr-open.sh' >/dev/null 2>&1; [ \$? = 2 ] && [ ! -s '$FAKE_LOG' ]"
|
||||
exit "$fail"
|
||||
Reference in new issue
Block a user