Compare commits

..
Author SHA1 Message Date
code-be-01 187d70be12 fix(git-tools): refuse no-ci assertion without an attributable identity
ci/woodpecker/pr/ci Pipeline was successful
The --no-ci-expected admin-success path accepted an asserted-by=unknown
pass when MOSAIC_GIT_IDENTITY was unset or empty, so the queue-clear
assertion and its NO_CI_ASSERTED audit record named no one. Refuse such
callers with a distinct ASSERTION_UNATTRIBUTABLE outcome (exit 78, audit
recorded) before queue-clear and before the permission lookup, so an
unattributable caller never triggers that network call. Document exit 78
in the usage text and the tools reference; pin the path with a
regression case (admin stub + env -u MOSAIC_GIT_IDENTITY).
2026-08-23 12:52:44 -05:00
code-be-01 28f4002a70 fix(git-tools): add admin-gated --no-ci-expected merge assertion to ci-queue-wait
A repository with no CI configured has no sanctioned wrapper merge
path: the queue guard fails closed on zero status contexts for
purpose=merge (exit 3). That fail-closed default stays correct, because
at merge time no-status may also mean CI has not reported yet; but it
left CI-less repos unmergeable without the break-glass override, which
makes the override a gap rather than an exception.

--no-ci-expected reclassifies only the zero-context case for merge as
queue-clear, and only when the acting token holds repository admin.
The elevation check reads the repository object's permissions.admin:
the branch-head and combined-status responses the guard already
fetches carry no permissions object at all. The assertion prints its
own audit line (purpose, branch, asserting identity, reason) and
writes a NO_CI_ASSERTED JSONL record to the existing audit sink; an
unauditable pass is refused (exit 70). A non-admin caller is refused
with exit 77 (ASSERTION_REFUSED, own text, audited), kept distinct
from ASSERTED_NOT_READY's exit 3; an unavailable permissions lookup
holds as CANNOT_ASSERT exit 75. --require-status contradicts the flag
and is a usage error; a pending or failed context still holds or
fails exactly as before; push behavior is unchanged.

pr-merge.sh gains a pass-through --no-ci-expected that only forwards
the flag to the guard invocation. PowerShell twins are unchanged: no
existing test exercises their guard path (pr-merge.ps1 only runs with
-SkipQueueGuard; ci-queue-wait.ps1 has no test).

Closes #1372
2026-08-23 12:34:11 -05:00
8 changed files with 6 additions and 437 deletions
+1 -8
View File
@@ -1,11 +1,4 @@
{
"schema_version": 2,
"integration_trunk": "next",
"release_branch": "main",
"flow": "trunk-release",
"canonical_remote": "https://git.mosaicstack.dev/mosaicstack/stack",
"canonical_clone": "host:/src/mosaic-stack",
"worktree_root": "host:/src/mosaic-stack-worktrees",
"worktree_policy": "orchestrator-precreated",
"notes": "next=development/integration; main=production release. Never branch work off main. worktree_policy is TRANSITIONAL: the wrapper worktree consumer is BLOCKED on the J3/#1174 amendment (checked roots + capacity guard); pre-creation is the interim orchestration choice, not closed policy — it becomes a timing choice only after the wrapper can validate this root."
"release_branch": "main"
}
@@ -379,54 +379,8 @@ check_fleet_transport() {
fi
}
check_structure_anchor_provisioning() {
# T51 WP0b (spec §1.2a + PHASE2-MAP F7): audit the two declaration anchors.
# Doctor runs from operator shells and CI where the launcher exports do not
# exist, so this is an AUDIT ONLY — it never exports, writes, or fabricates
# values for consumption. Four states (charter):
# both present+nonempty PASS (values reported as paths only)
# one missing/empty WARN naming the var + the launcher as authority
# neither present INFORMATIONAL launcher-equivalent derivation,
# explicitly non-authoritative, + launcher warning;
# never an error by design (F7(b))
# Severity follows the doctor's existing conventions: pass/note are quiet
# (note unless --verbose), warn counts toward --fail-on-warn.
local host_root="${MOSAIC_HOST_ROOT:-}" brain_home="${MOSAIC_BRAIN_HOME:-}"
# T51P2WP0BRW B1: presence is tracked SEPARATELY from value — `${VAR:-}`
# collapses exported-empty into genuinely-unset, which mis-filed both-empty
# and the mixed empty/unset states as informational. Only BOTH-genuinely-
# absent may be informational (charter state 3); any present-but-empty or
# single-present state warns.
local host_set=0 brain_set=0
[[ -v MOSAIC_HOST_ROOT ]] && host_set=1
[[ -v MOSAIC_BRAIN_HOME ]] && brain_set=1
if [[ "$host_set" -eq 1 && "$brain_set" -eq 1 && -n "$host_root" && -n "$brain_home" ]]; then
pass "Structure anchors provisioned: MOSAIC_HOST_ROOT=$host_root MOSAIC_BRAIN_HOME=$brain_home (paths reported only; not expanded, not consumed)"
return
fi
if [[ "$host_set" -eq 0 && "$brain_set" -eq 0 ]]; then
note "Structure anchors not provisioned in this environment. Launcher-equivalent derivation (INFORMATIONAL, NON-AUTHORITATIVE — seats receive the authoritative values from the launchers): MOSAIC_HOST_ROOT would default to the operator home; MOSAIC_BRAIN_HOME would default to the brain tree resolved at launch. Doctor does not guess values for consumption; it audits provisioning."
note "Provision both anchors via the seat launchers (launch-seat.sh / launch-seat-claude.sh export them; see T51 spec §1.2a)."
return
fi
# At least one variable is present (possibly empty), or exactly one exists:
# every missing/empty anchor gets its own loud WARN naming the launchers.
if [[ "$host_set" -eq 0 ]]; then
warn "MOSAIC_HOST_ROOT is not set in this environment while MOSAIC_BRAIN_HOME is — declaration consumers fail closed without it (spec §1.2a). The seat launchers are the authoritative source."
elif [[ -z "$host_root" ]]; then
warn "MOSAIC_HOST_ROOT is present but EMPTY in this environment — declaration consumers fail closed without a usable value (spec §1.2a). The seat launchers are the authoritative source."
fi
if [[ "$brain_set" -eq 0 ]]; then
warn "MOSAIC_BRAIN_HOME is not set in this environment while MOSAIC_HOST_ROOT is — the projects/ mirror and brain declaration resolve from it (spec §1.2a). The seat launchers are the authoritative source."
elif [[ -z "$brain_home" ]]; then
warn "MOSAIC_BRAIN_HOME is present but EMPTY in this environment — the projects/ mirror and brain declaration resolve from it (spec §1.2a). The seat launchers are the authoritative source."
fi
}
check_fleet_transport
check_structure_anchor_provisioning
check_brain_home
# Legacy migration surfaces should no longer contain symlink trees.
@@ -1,131 +0,0 @@
#!/usr/bin/env bash
# Covers the structure-anchor provisioning check in `mosaic-doctor` (T51 WP0b).
#
# Same discipline as test-brain-home-check.sh: functions are extracted from the
# shipped script (exact header + closing brace), never copied — a test carrying
# its own copy of the logic keeps passing after the shipped copy changes.
#
# Four contract states (charter T51P2WP0B-20260824):
# 1. both present+nonempty -> pass ([OK]), no warns, no notes
# 2a. host missing, brain set -> warn naming MOSAIC_HOST_ROOT + launchers
# 2b. brain missing, host set -> warn naming MOSAIC_BRAIN_HOME + launchers
# 2c. present-but-EMPTY counts as missing (warns; NEVER informational)
# 3. neither present -> informational notes, NON-AUTHORITATIVE, never warn
# Arms include genuinely-UNSET (env -u) forms, not only empty strings.
# Red control: empty-vs-unset distinction removed in a mutated copy -> suite red.
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname "$0")" && pwd)
DOCTOR="$SCRIPT_DIR/mosaic-doctor"
fail() {
echo "FAIL: $*" >&2
exit 1
}
[ -f "$DOCTOR" ] || fail "missing mosaic-doctor at $DOCTOR"
extract_function() {
local name="$1"
local extracted
extracted=$(sed -n "/^${name}() {/,/^}/p" "$DOCTOR")
[ -n "$extracted" ] || fail "could not extract ${name}() from mosaic-doctor — script reshaped?"
printf '%s\n' "$extracted"
}
for fn in check_structure_anchor_provisioning; do
extract_function "$fn" >/dev/null
done
# run_case LABEL EXPECT(ok|warn|note) [env assignments as args; -u VAR tokens for unset]
run_case() {
local label="$1" expect="$2"
shift 2
local envs=() unsets=()
local a
for a in "$@"; do
case "$a" in
-u:*) unsets+=("${a#-u:}") ;;
*) envs+=("$a") ;;
esac
done
local out warns notes oks
# build the env command with proper -u flags (array expansion must not
# glue '-u VAR' into one word)
local cmd=(env)
local e u
# env(1) parses options only before the first assignment — -u flags FIRST
for u in "${unsets[@]:-}"; do [ -n "$u" ] && cmd+=(-u "$u"); done
for e in "${envs[@]:-}"; do [ -n "$e" ] && cmd+=("$e"); done
cmd+=(bash -c "warn() { echo \"[WARN] \$*\"; }; note() { echo \"[NOTE] \$*\"; return 0; }; pass() { echo \"[OK] \$*\"; return 0; }; $(extract_function check_structure_anchor_provisioning); check_structure_anchor_provisioning")
out=$("${cmd[@]}" 2>&1)
warns=$(printf '%s\n' "$out" | grep -c '^\[WARN\]' || true)
notes=$(printf '%s\n' "$out" | grep -c '^\[NOTE\]' || true)
oks=$(printf '%s\n' "$out" | grep -c '^\[OK\]' || true)
if [[ "$expect" == ok && "$oks" -gt 0 && "$warns" -eq 0 && "$notes" -eq 0 ]]; then
echo "ok - $label"
elif [[ "$expect" == warn && "$warns" -ge 1 && "$notes" -eq 0 ]]; then
echo "ok - $label (warned x$warns)"
elif [[ "$expect" == note && "$notes" -gt 0 && "$warns" -eq 0 ]]; then
echo "ok - $label (noted)"
else
echo "output: $out" >&2
fail "$label: expected $expect (oks=$oks warns=$warns notes=$notes)"
fi
}
ROOT=$(mktemp -d)
trap 'rm -rf "$ROOT"' EXIT
HOST="$ROOT/host"
BRAIN="$ROOT/brain"
# ── state 1: both present + nonempty → pass ────────────────────────────────
run_case "both anchors present passes" ok \
MOSAIC_HOST_ROOT="$HOST" MOSAIC_BRAIN_HOME="$BRAIN"
# ── state 2a: host missing (unset), brain set → exactly one warn ───────────
run_case "unset host root warns" warn \
-u:MOSAIC_HOST_ROOT MOSAIC_BRAIN_HOME="$BRAIN"
# ── state 2b: brain missing (unset), host set → exactly one warn ───────────
run_case "unset brain home warns" warn \
MOSAIC_HOST_ROOT="$HOST" -u:MOSAIC_BRAIN_HOME
# ── state 2c-empty: present-but-empty counts as missing ────────────────────
run_case "empty-string host root warns (empty != set)" warn \
MOSAIC_HOST_ROOT= MOSAIC_BRAIN_HOME="$BRAIN"
run_case "empty-string brain home warns (empty != set)" warn \
MOSAIC_HOST_ROOT="$HOST" MOSAIC_BRAIN_HOME=
# ── state 3: neither present (genuinely unset) → notes, never warn ─────────
run_case "both unset yields non-authoritative notes" note \
-u:MOSAIC_HOST_ROOT -u:MOSAIC_BRAIN_HOME
run_case "both empty-string warns (empty is present, not absent)" warn \
MOSAIC_HOST_ROOT= MOSAIC_BRAIN_HOME=
run_case "host empty + brain unset warns" warn \
MOSAIC_HOST_ROOT= -u:MOSAIC_BRAIN_HOME
run_case "host unset + brain empty warns" warn \
-u:MOSAIC_HOST_ROOT MOSAIC_BRAIN_HOME=
# ── red control (mutation): presence tracking removed → red ────────────────
# Mutant regresses to the reviewed defect shape: presence derived from
# NONEMPTINESS (the `${VAR:-}` collapse) instead of true -v tracking. Both-empty
# then looks genuinely-absent and is mis-filed as informational; the both-empty
# warn arm above finds no WARN and the suite reds.
MUT="$ROOT/mosaic-doctor.mutant"
sed 's/\[\[ -v MOSAIC_HOST_ROOT \]\] \&\& host_set=1/[[ -n "${MOSAIC_HOST_ROOT:-}" ]] \&\& host_set=1/; s/\[\[ -v MOSAIC_BRAIN_HOME \]\] \&\& brain_set=1/[[ -n "${MOSAIC_BRAIN_HOME:-}" ]] \&\& brain_set=1/' \
"$DOCTOR" > "$MUT"
if cmp -s "$DOCTOR" "$MUT"; then
echo "SKIP red control (mutation anchor not found — sed pattern drifted)" >&2
else
mut_fn=$(sed -n "/^check_structure_anchor_provisioning() {/,/^}/p" "$MUT")
outm=$(env MOSAIC_HOST_ROOT= MOSAIC_BRAIN_HOME= bash -c \
"warn() { echo \"[WARN] \$*\"; }; note() { echo \"[NOTE] \$*\"; return 0; }; pass() { echo \"[OK] \$*\"; return 0; }; $mut_fn; check_structure_anchor_provisioning" 2>&1)
if printf '%s\n' "$outm" | grep -q '^\[NOTE\]'; then
echo "ok - red control bites (mutant collapses empty into informational; shipped does not)"
else
fail "red control did not reproduce the regression shape (mutant output unexpected)"
fi
fi
echo "structure anchor doctor check: all arms passed"
@@ -19,41 +19,6 @@ ISSUE=""
# get_remote_host, get_gitea_token, get_repo_info, and get_gitea_repo_args are provided by detect-platform.sh
gitea_default_branch() {
# Forge default branch for the current repo (T51-P2 WP5a / spec E4): the
# API fallback must not guess a base. Empty output or any lookup failure
# returns nonzero so the caller fails loud instead of mistargeting a PR.
local host repo token url body branch
host=$(get_remote_host) || return 1
repo=$(get_repo_info) || return 1
token=$(get_gitea_token "$host") || return 1
url="https://${host}/api/v1/repos/${repo}"
# Fetch and parse as separate steps (T51P2WP5AR B2): a piped
# `curl | python` reports only python's status, so an HTTP failure that
# still emits parseable JSON would masquerade as success. curl's own
# exit status is authoritative here.
if ! body=$(curl -fsS \
-H "User-Agent: curl/8" \
-H "Authorization: token ${token}" \
"$url" 2>/dev/null); then
return 1
fi
# A valid base is a NONBLANK JSON STRING (T51P2WP5AR B3): null, numbers,
# and whitespace-only values are failed resolution, never a POSTed base.
branch=$(printf '%s' "$body" | python3 -c '
import json, sys
try:
value = json.load(sys.stdin).get("default_branch")
except Exception:
sys.exit(1)
if not isinstance(value, str) or not value.strip():
sys.exit(1)
print(value.strip())
' 2>/dev/null) || return 1
[[ -n "$branch" ]] || return 1
printf '%s' "$branch"
}
gitea_pr_create_api() {
local host repo token url payload
host=$(get_remote_host) || {
@@ -73,28 +38,14 @@ gitea_pr_create_api() {
echo "Warning: API fallback applies title/body/head/base only; labels/milestone/draft require authenticated tea setup." >&2
fi
# Base resolution (spec E4): an explicit -B always wins; with none, the
# forge default branch is resolved from the provider API -- never the
# historical "main" literal, which mistargeted every fallback PR on
# repos whose trunk is not main (e.g. mosaicstack/stack -> next).
local api_base=""
if [[ -n "$BASE_BRANCH" ]]; then
api_base="$BASE_BRANCH"
else
api_base=$(gitea_default_branch) || {
echo "Error: could not resolve the forge default branch for the API-fallback base; pass -B <branch> explicitly" >&2
return 1
}
fi
payload=$(TITLE="$TITLE" BODY="$BODY" HEAD_BRANCH="$HEAD_BRANCH" API_BASE="$api_base" python3 - <<'PY'
payload=$(TITLE="$TITLE" BODY="$BODY" HEAD_BRANCH="$HEAD_BRANCH" BASE_BRANCH="$BASE_BRANCH" python3 - <<'PY'
import json
import os
payload = {
"title": os.environ["TITLE"],
"head": os.environ["HEAD_BRANCH"],
"base": os.environ["API_BASE"],
"base": os.environ["BASE_BRANCH"] or "main",
}
body = os.environ.get("BODY", "")
if body:
@@ -121,7 +72,7 @@ Create a pull request on the current repository (Gitea or GitHub).
Options:
-t, --title TITLE PR title (required, or use --issue)
-b, --body BODY PR description/body
-B, --base BRANCH Base branch to merge into (default: the forge repository's default branch)
-B, --base BRANCH Base branch to merge into (default: main/master)
-H, --head BRANCH Head branch with changes (default: current branch)
-l, --labels LABELS Comma-separated labels
-m, --milestone NAME Milestone name
@@ -100,15 +100,6 @@ case "$url" in
*/commits/*/status)
printf '{"state":"success","statuses":[{"context":"ci/mock","status":"success"}]}'
;;
# Repo roots: the pr-create API fallback resolves its base from the forge
# default_branch (T51-P2 WP5a). Exact-suffix matches so the /pulls POST
# endpoint (no trailing path) still falls through to the catch-all.
*/api/v1/repos/USC/uconnect)
printf '{"default_branch":"main"}'
;;
*/api/v1/repos/mosaicstack/stack)
printf '{"default_branch":"next"}'
;;
*)
printf '{}'
;;
@@ -67,18 +67,7 @@ cat > "$BIN_DIR/curl" <<'SH'
#!/usr/bin/env bash
set -euo pipefail
printf 'curl %s\n' "$*" >> "$MOSAIC_TEST_LOG"
# Repo roots: the pr-create API fallback resolves its base from the forge
# default_branch (T51-P2 WP5a). Exact-suffix so every other endpoint keeps
# the historical answer below.
url="${*: -1}"
case "$url" in
*/api/v1/repos/mosaicstack/stack)
printf '%s\n' '{"default_branch":"next"}'
;;
*)
printf '%s\n' '{"number":703}'
;;
esac
printf '%s\n' '{"number":703}'
SH
chmod +x "$BIN_DIR/tea" "$BIN_DIR/curl"
@@ -1,178 +0,0 @@
#!/usr/bin/env bash
# test-pr-create-fallback-default-base.sh — hermetic test for the API-fallback
# base resolution in pr-create.sh (T51-P2 WP5a / spec E4).
#
# The API-fallback payload historically hardcoded "base": "main", mistargeting
# every fallback PR on repos whose trunk is not main (e.g. mosaicstack/stack,
# default branch "next"). The fix: an explicit -B always wins; with none, the
# base is resolved from the provider API default_branch, and a failed
# resolution fails loud instead of guessing.
#
# Hermetic by construction: every curl invocation is a PATH-first stub; the
# fixture repo's remote is git.example.test (never dialed); HOME is a sandbox
# with no tea config (so the wrapper takes the API fallback path); GITEA_TOKEN
# comes from the environment. No real forge is contacted.
# shellcheck disable=SC2317
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/pr-create-fallback-base}"
PASS=0 FAIL=0 FAILED_CASES=""
ok() { PASS=$((PASS + 1)); }
bad() { FAIL=$((FAIL + 1)); FAILED_CASES="$FAILED_CASES $1"; printf 'FAIL: %s\n' "$1" >&2; }
assert_rc() { local d="$1" e="$2" a="$3"; [ "$e" = "$a" ] && ok || bad "$d (expected rc=$e got rc=$a)"; }
assert_eq() { local d="$1" e="$2" a="$3"; [ "$e" = "$a" ] && ok || bad "$d (expected [$e] got [$a])"; }
assert_contains() { local d="$1" h="$2" n="$3"; case "$h" in *"$n"*) ok ;; *) bad "$d (missing [$n])" ;; esac; }
json_field() { # $1 payload file, $2 field
python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get(sys.argv[2], ""))' "$1" "$2"
}
rm -rf "$WORK_DIR"
# ---- fixture -----------------------------------------------------------------
ROOT="$WORK_DIR/fixture"
TOOLS="$ROOT/tools/git"
mkdir -p "$TOOLS" "$ROOT/repo" "$ROOT/home" "$ROOT/stub"
cp "$SCRIPT_DIR/pr-create.sh" "$TOOLS/pr-create.sh"
cp "$SCRIPT_DIR/detect-platform.sh" "$TOOLS/detect-platform.sh"
git -C "$ROOT/repo" init -q -b fix/e4
git -C "$ROOT/repo" -c user.name=fixture -c user.email=fixture@test commit -q --allow-empty -m base
git -C "$ROOT/repo" remote add origin https://git.example.test/acme/widgets.git
# curl stub: GET repo -> default_branch JSON (or failure mode); POST pulls ->
# capture payload, answer with a minimal PR JSON. Every call is logged.
cat > "$ROOT/stub/curl" <<STUB
#!/usr/bin/env bash
set -u
mode="\${CURL_STUB_GET_MODE:-ok}"
printf '%s\n' "\$*" >> "$ROOT/curl-calls.log"
url="\${!#}"
if [[ "\$url" == */api/v1/repos/acme/widgets ]]; then
# repo GET (default-branch resolution). Modes cover the value shapes the
# resolver must accept or refuse (T51P2WP5AR B2/B3).
case "\$mode" in
ok) printf '%s\n' '{"id":1,"default_branch":"next","full_name":"acme/widgets"}' ;;
fail) echo "curl stub: simulated repo lookup failure" >&2; exit 1 ;;
fail-json) printf '%s\n' '{"default_branch":"next"}'; exit 22 ;;
null) printf '%s\n' '{"default_branch":null}' ;;
numeric) printf '%s\n' '{"default_branch":7}' ;;
blank) printf '%s\n' '{"default_branch":" "}' ;;
*) echo "curl stub: unknown GET mode \$mode" >&2; exit 1 ;;
esac
exit 0
fi
if [[ "\$url" == */api/v1/repos/acme/widgets/pulls ]]; then
# PR POST: capture the payload, emit a PR-shaped answer
while [[ \$# -gt 0 ]]; do
case "\$1" in
-d) printf '%s' "\$2" > "$ROOT/payload.json"; shift 2 ;;
*) shift ;;
esac
done
printf '%s\n' '{"number":42,"html_url":"https://git.example.test/acme/widgets/pulls/42"}'
exit 0
fi
echo "curl stub: unexpected URL \$url" >&2
exit 1
STUB
chmod +x "$ROOT/stub/curl"
run_pr_create() { # args... -> sets RC/OUT/ERR
RC=0
OUT=$(cd "$ROOT/repo" && env -i \
PATH="$ROOT/stub:/usr/bin:/bin" \
HOME="$ROOT/home" \
GITEA_TOKEN=stub-token \
bash "$TOOLS/pr-create.sh" "$@" 2>"$ROOT/err.txt")
RC=$?
ERR="$(cat "$ROOT/err.txt")"
}
calls_matching() { grep -c -- "$1" "$ROOT/curl-calls.log" 2>/dev/null || true; }
echo "== (1) no -B: fallback base resolves to the forge default branch, not main =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
run_pr_create -t "fix thing"
assert_rc "rc" 0 "$RC"
assert_contains "API fallback path taken (tea login unresolvable in fixture)" "$ERR" "trying Gitea API fallback"
assert_eq "repo GET performed" 1 "$(calls_matching '/api/v1/repos/acme/widgets$')"
assert_eq "POST performed" 1 "$(calls_matching '/pulls$')"
assert_eq "payload base is forge default (next)" "next" "$(json_field "$ROOT/payload.json" base)"
assert_eq "payload head" "fix/e4" "$(json_field "$ROOT/payload.json" head)"
assert_eq "payload title" "fix thing" "$(json_field "$ROOT/payload.json" title)"
echo "== (2) explicit -B wins; the default branch is not consulted =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
run_pr_create -t "fix thing" -B release/1.x
assert_rc "rc" 0 "$RC"
assert_eq "repo GET not consulted for explicit base" 0 "$(calls_matching '/api/v1/repos/acme/widgets$')"
assert_eq "payload base is the explicit -B" "release/1.x" "$(json_field "$ROOT/payload.json" base)"
echo "== (3) default-branch lookup failure: loud refusal, no POST =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
RC=0
OUT=$(cd "$ROOT/repo" && env -i \
PATH="$ROOT/stub:/usr/bin:/bin" \
HOME="$ROOT/home" \
GITEA_TOKEN=stub-token \
CURL_STUB_GET_MODE=fail \
bash "$TOOLS/pr-create.sh" -t "fix thing" 2>"$ROOT/err.txt")
RC=$?
ERR="$(cat "$ROOT/err.txt")"
assert_rc "nonzero rc on unresolvable base" 1 "$RC"
assert_contains "loud error names -B" "$ERR" "could not resolve the forge default branch"
assert_contains "error names the remedy" "$ERR" "pass -B <branch> explicitly"
assert_eq "no POST issued" 0 "$(calls_matching '/pulls$')"
echo "== (4) payload never contains the literal fallback main =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
run_pr_create -t "fix thing"
assert_rc "rc" 0 "$RC"
assert_eq "base field is next, never main" "next" "$(json_field "$ROOT/payload.json" base)"
echo "== (5) B2: HTTP failure with parseable JSON on stdout is a FAILED resolution =="
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
RC=0
OUT=$(cd "$ROOT/repo" && env -i \
PATH="$ROOT/stub:/usr/bin:/bin" \
HOME="$ROOT/home" \
GITEA_TOKEN=stub-token \
CURL_STUB_GET_MODE=fail-json \
bash "$TOOLS/pr-create.sh" -t "fix thing" 2>"$ROOT/err.txt")
RC=$?
ERR="$(cat "$ROOT/err.txt")"
assert_rc "nonzero rc on HTTP failure despite valid JSON" 1 "$RC"
assert_contains "loud error names -B" "$ERR" "could not resolve the forge default branch"
assert_contains "error names the remedy" "$ERR" "pass -B <branch> explicitly"
assert_eq "no POST issued" 0 "$(calls_matching '/pulls$')"
echo "== (6) B3: null / numeric / blank default_branch are failed resolutions =="
for bad in null numeric blank; do
: > "$ROOT/curl-calls.log"; rm -f "$ROOT/payload.json"
RC=0
OUT=$(cd "$ROOT/repo" && env -i \
PATH="$ROOT/stub:/usr/bin:/bin" \
HOME="$ROOT/home" \
GITEA_TOKEN=stub-token \
CURL_STUB_GET_MODE="$bad" \
bash "$TOOLS/pr-create.sh" -t "fix thing" 2>"$ROOT/err.txt")
RC=$?
ERR="$(cat "$ROOT/err.txt")"
assert_rc "B3 $bad: nonzero rc" 1 "$RC"
assert_contains "B3 $bad: loud error" "$ERR" "could not resolve the forge default branch"
assert_eq "B3 $bad: no POST issued" 0 "$(calls_matching '/pulls$')"
done
echo
echo "pass=$PASS fail=$FAIL"
if [ "$FAIL" -gt 0 ]; then
echo "FAILED CASES:$FAILED_CASES"
exit 1
fi
echo "ALL GREEN"
+1 -1
View File
@@ -25,7 +25,7 @@
"lint": "eslint src",
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests && pnpm run test:framework-shell",
"test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 framework/tools/quality/scripts/test-framework-drift-check.py && bash framework/tools/quality/scripts/test-framework-drift-doctor.sh && bash framework/systemd/user/test-fleet-units.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/promotion_binding_unittest.py && python3 src/lease-broker/promotion_trigger_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/receipt_observer_client_unittest.py && python3 src/lease-broker/invariant_r_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/lease-broker/revoke_noop_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-edit.sh && bash framework/tools/git/test-pr-create-fallback-default-base.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-no-status.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-ci-queue-wait-no-ci-expected.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-no-ci-expected.sh && bash framework/tools/git/test-pr-merge-fork-ci-status.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/git/test-explain-diagnostic-status-neutral.sh && bash framework/tools/git/test-detect-platform-outside-repo.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/_scripts/test-mosaic-init-rce.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh && bash framework/tools/glpi/test-list-http-status.sh && bash framework/tools/orchestrator/test-board-roll.sh && bash framework/tools/woodpecker/test-ci-wait-exit-matrix.sh && bash framework/tools/_scripts/test-fleet-transport-check.sh && bash framework/tools/_scripts/test-brain-home-check.sh && bash framework/tools/_scripts/test-structure-anchor-check.sh && bash framework/tools/fleet/test-agent-session-broker-preflight.sh"
"test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 framework/tools/quality/scripts/test-framework-drift-check.py && bash framework/tools/quality/scripts/test-framework-drift-doctor.sh && bash framework/systemd/user/test-fleet-units.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/promotion_binding_unittest.py && python3 src/lease-broker/promotion_trigger_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/receipt_observer_client_unittest.py && python3 src/lease-broker/invariant_r_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/lease-broker/revoke_noop_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-edit.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-no-status.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-ci-queue-wait-no-ci-expected.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-no-ci-expected.sh && bash framework/tools/git/test-pr-merge-fork-ci-status.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/git/test-explain-diagnostic-status-neutral.sh && bash framework/tools/git/test-detect-platform-outside-repo.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/_scripts/test-mosaic-init-rce.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh && bash framework/tools/glpi/test-list-http-status.sh && bash framework/tools/orchestrator/test-board-roll.sh && bash framework/tools/woodpecker/test-ci-wait-exit-matrix.sh && bash framework/tools/_scripts/test-fleet-transport-check.sh && bash framework/tools/_scripts/test-brain-home-check.sh && bash framework/tools/fleet/test-agent-session-broker-preflight.sh"
},
"dependencies": {
"@mosaicstack/brain": "workspace:*",