* fix(scripts): bound release CI summary GitHub lookups
The release CI summary helper performs multiple sequential gh API reads
during full release validation. Each read used execFileSync without a
timeout, so one stalled GitHub request could block the surrounding
workflow job until it expires.
Route the default gh runner through a caller-owned execFileSync with a
60-second timeout and SIGKILL, and apply the same bound to artifact zip
downloads. The runner accepts an execFileSyncImpl injection so the
timeout contract can be tested without a slow timer-based test.
* test(scripts): add real behavior proof for release CI summary timeout
Add two live subprocess tests alongside the existing mocked tests:
- A 1 ms timeout against a real gh subprocess proves the SIGKILL +
timeout surface works on actual child processes (not only mocks).
- A fast node -e 0 subprocess with the production 60-second bound
proves the timeout does not false-positive on healthy completions.
The runReleaseCiGh runner now accepts an optional timeoutMs parameter
so tests can exercise a shorter timeout without changing the production
default.
* refactor(scripts): share bounded release GitHub runner
* test(scripts): keep release runner assertions type-safe
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(mantis): bound upload error response bodies
Replace unbounded response.text() on non-ok upload error paths with
readBoundedResponseText (64 KiB cap). Only swallow the size-exceeded
diagnostic; propagate signal aborts and other failures so timeouts
during body reading are not masked as generic upload failures.
- Import shared readBoundedResponseText from scripts/lib/bounded-response.mjs
- Add MANTIS_UPLOAD_ERROR_BODY_MAX_BYTES constant (64 KiB)
- Catch only size-exceeded errors; re-throw timeouts and stream errors
- Add test: oversized body bounded at 64 KiB
- Add test: signal abort during body read propagates correctly
- Add test: small error body within bound reads normally
* fix(mantis): contextualize bounded upload failures
Co-authored-by: 胡根深 0668000903 <hu.genshen@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Live verification showed dropped-CI PRs report mergeable=null and
mergeable_state=unknown indefinitely: the stuck merge-ref computation is
the same failure that dropped their CI, and close/reopen is what
un-sticks it. The pending-mergeability skip therefore made the primary
repair population permanently unsweepable (three real dropped PRs skipped
across three consecutive sweeps). Keep skipping computed conflicts; a
not-yet-computed conflict costs at most one budgeted re-fire.
* ci: add hourly PR CI sweeper for dropped pull_request runs
Fresh PRs race GitHub's merge-ref computation: the open-event CI run can
drop entirely or be created as an un-rerunnable startup_failure (~10-16
runs daily). The sweeper lists recently updated open PRs hourly, finds
heads whose only pull_request-event CI runs are startup failures (or
missing), and re-fires the event by close/reopen with the Barnacle app
token (GITHUB_TOKEN events would not trigger workflows).
Safety: 10-minute quiet window, 24h lookback, skips drafts, merge
conflicts, pending mergeability, and auto-merge PRs (close cancels
auto-merge); revalidates state, head, and CI attachment immediately
before mutating; per-PR budget of two sweeper closes and a per-sweep cap
of ten; reopen-on-unknown ownership so a stranded close (silent) always
loses to a spurious reopen (visible); manual dispatch supports dry_run.
Accepted tradeoffs are documented inline: shared-SHA PR topologies can
mask a dropped run (skip-only miss; run.pull_requests matching would
break fork PRs), and app-auth failover at worst doubles the close budget.
* test(ci): exercise pr-ci-sweeper runner with a faked client
* fix(test): lint-clean pr-ci-sweeper runner fakes
* fix(scripts): restore Testbox lease claims to the originating repo after delegated runs
Delegated Blacksmith Testbox runs execute from throwaway sparse-sync checkouts, so Crabbox stamps retained lease claims with that temporary cwd. Restore only claims still pointing at the child checkout after the run on both success and failure, preserving foreign claims and every other field. This repairs the post-run artifact instead of weakening the pre-run ownership guard.
* fix(scripts): restore claims for delegated runs that create retained leases
* fix(ci): bound all openclaw-npm-release git fetch operations with timeout
Wrap all six git fetch commands with a 120-second deadline
and 10-second termination grace period, matching the pattern
established in #108940.
Fixes#109176
* test(ci): cover npm release fetch deadlines
Co-authored-by: wanyongstar <wan.yong@xydigit.com>
* ci: raise Control UI startup budget
Unblock exact-head builds after the compressed startup bundle reached the previous 370 KiB ratchet.
* ci: drop superseded Control UI budget bump
Current main now carries the smaller 340 KiB startup ratchet from #110528, so remove the temporary 372 KiB adjustment while keeping the PR head conflict-free.
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(ci): bound CLI installer smoke curl with connect and max-time
The non-root CLI installer smoke pipes curl directly into bash under a
single docker-run timeout. curl has no default connect or total timeout,
so a stalled CDN connect or hung response body could pin the container
until the outer docker-run budget kills it. Bound the curl with the same
--connect-timeout 30 --max-time 300 shape already used by install-sh-smoke
and install-sh-e2e, and enable pipefail so curl failures are not masked
by the bash consumer.
* test(ci): cover bounded CLI installer pipeline
* fix(ci): bound preceding non-root installer download
* fix(ci): harden bounded installer URL handoff
Co-authored-by: zhang-guiping <zhang.guiping@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* perf(ci): cut the pre-fan-out critical path on canonical runs
Two changes to the run head and matrix shape:
1. runner-admission was a hosted 90s sleep every run queued behind
(observed 1.7min hosted-queue latency before the sleep started). The
debounce now lives at the tail of preflight: heavy jobs all need
preflight, so a superseding main push still cancels the run before
fan-out while only one 4 vCPU runner has been spent, and preflight's
own work usually exceeds the window so the residual sleep is zero.
security-fast (hosted, dependency-free) starts immediately.
2. Canonical main pushes now use the compact bin plan like PRs: the
82-job named matrix drained the runner pool for ~4.5min (job starts
trickled from minute 5.2 to 9.7 in run 29592647843) with no
branch-protection consumer for per-shard names on main. Coverage is
identical; dispatch/release-gate runs keep the full named matrix.
* perf(test): boot TUI PTY suite fixtures concurrently
tsx+TUI startup dominated the harness file's wall time and the three
suite PTYs booted serially. Boot them concurrently in beforeAll
(allSettled so a failed boot still assigns survivors for afterAll
cleanup); the env-specific fixtures never receive input, so their tests
only await their own readiness output. The slow-startup test now proves
frame ordering on the append-only output, which the old sequential
waits did not. File wall 10.2s -> ~4.9s, 3/3 repeat runs green.
* docs(ci): align gate and debounce descriptions with the removed admission job
* test(tooling): wait for readiness file content, not existence
writeFileSync creates the file before its bytes land, so the existence
poll raced the child's write on loaded runners and read an empty ready
file (observed in compact-small-4, run 29615028678). Poll for non-empty
content at both readiness sites.