From 46c6e5a9c3f1a44eab78924e294ded60a8f5a0b6 Mon Sep 17 00:00:00 2001 From: Piers Cockram Date: Thu, 28 May 2026 12:57:01 +1000 Subject: [PATCH] breakglass-sync: per-owner soft budget (OWNER_BUDGET_SEC, default 2h) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once an owner consumes more than OWNER_BUDGET_SEC seconds, the script stops starting new repos for that owner and moves on to the next. The in-flight repo finishes naturally — we never kill mid-push. ONLY_REPO mode bypasses the budget (single-repo runs are intentional). Surfaced via a new OWNERS_DEFERRED counter in the summary line + an OWNER_BUDGET_EXCEEDED audit event per owner. Originally added after signalapp was starved by openclaw repeatedly consuming the whole window. With 12 owners and a 16h sync timeout, 2h/owner gives ample headroom while preventing any single owner from monopolising the run. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/breakglass-sync.sh | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/scripts/breakglass-sync.sh b/scripts/breakglass-sync.sh index 3db310c..749c27d 100644 --- a/scripts/breakglass-sync.sh +++ b/scripts/breakglass-sync.sh @@ -58,6 +58,12 @@ SYNC_RELEASES="${SYNC_RELEASES:-true}" REVERSE_SYNC_REPOS="${REVERSE_SYNC_REPOS:-}" GITHUB_PUSH_TOKEN="${GITHUB_PUSH_TOKEN:-}" GITHUB_PUSH_OWNER="${GITHUB_PUSH_OWNER:-}" +# Per-owner soft budget. Once exceeded, the script stops starting new repos +# for that owner and moves on to the next. The in-flight repo (if any) +# finishes naturally — we never kill mid-push. Set to 0 to disable. +# Default 7200s (2h). Originally added 2026-05-28 after signalapp was +# starved by openclaw repeatedly consuming the whole window. +OWNER_BUDGET_SEC="${OWNER_BUDGET_SEC:-7200}" mkdir -p "$MIRROR_ROOT" "$RELEASE_ROOT" "$LOG_DIR" "$AUDIT_DIR" @@ -68,6 +74,7 @@ ERRORS=0 SYNCED=0 SKIPPED=0 PROTECTED=0 +OWNERS_DEFERRED=0 # ── Helpers ────────────────────────────────────────────── @@ -1092,9 +1099,24 @@ if [[ -z "$REVERSE_SYNC_ONLY" ]]; then repos=$(gh_list_repos "$gh_owner") || { (( ERRORS++ )) || true; continue; } fi + owner_start=$(date +%s) + while IFS= read -r repo; do [[ -z "$repo" ]] && continue + # Per-owner budget: stop starting new repos once exceeded. + # The in-flight repo finishes naturally; we just don't begin another. + # ONLY_REPO mode bypasses the budget (single-repo runs are intentional). + if [[ -z "$ONLY_REPO" ]] && (( OWNER_BUDGET_SEC > 0 )); then + owner_elapsed=$(( $(date +%s) - owner_start )) + if (( owner_elapsed > OWNER_BUDGET_SEC )); then + warn "owner $gh_owner exceeded budget (${owner_elapsed}s > ${OWNER_BUDGET_SEC}s) — deferring remaining repos to next run" + audit "OWNER_BUDGET_EXCEEDED owner=$gh_owner elapsed=${owner_elapsed}s budget=${OWNER_BUDGET_SEC}s" + (( OWNERS_DEFERRED++ )) || true + break + fi + fi + if [[ -z "$ONLY_REPO" ]]; then if ! matches_glob "$repo" "${include_glob:-*}"; then (( SKIPPED++ )) || true; continue @@ -1126,7 +1148,7 @@ fi DURATION=$(( $(date +%s) - $(date -d "$TIMESTAMP" +%s 2>/dev/null || date -u -d "${TIMESTAMP:0:8} ${TIMESTAMP:9:2}:${TIMESTAMP:11:2}:${TIMESTAMP:13:2}" +%s 2>/dev/null || echo 0) )) DURATION_MIN=$(( DURATION / 60 )) -SUMMARY="Breakglass sync: ${SYNCED} synced, ${SKIPPED} skipped, ${PROTECTED} wipe-protected, ${ERRORS} errors (${DURATION_MIN}m)" +SUMMARY="Breakglass sync: ${SYNCED} synced, ${SKIPPED} skipped, ${PROTECTED} wipe-protected, ${ERRORS} errors, ${OWNERS_DEFERRED} owners deferred (${DURATION_MIN}m)" log "" log "═══ $SUMMARY ═══" audit "SESSION_END $SUMMARY"