* chore(pr): reuse green hosted CI from patch-identical pre-rebase heads (24h window)
* fix(pr): type-safe fixtures and lint cleanup for hosted-CI reuse tests
* feat(sessions): retain reset history in sqlite with physical disk budget
* fix(sessions): satisfy test-types, knip export scan, and docs map
* fix(sessions): align behavioral suites and flip proof with retained history, route-aware cleanup plans
* fix(scripts): bound duplicate PR closure GitHub lookups
The duplicate PR closure helper performs multiple sequential gh API
reads and writes after a merge. Each call used execFileSync without a
timeout, so one stalled GitHub request could block the surrounding
workflow job until it expires.
Add a 60-second timeout and SIGKILL to the default gh runner, applied
to both read-only lookups and pr edit writes that forward stdin.
* test(scripts): add real behavior proof for duplicate PR closure timeout
Add two live subprocess tests using the real spawnSync (not mocked) to
prove the timeout + SIGKILL surface works against actual child
processes:
- A 1 ms timeout against a long-running node subprocess proves
SIGKILL is delivered and the timeout triggers correctly.
- A fast node -e 0 subprocess with the production 60-second bound
proves the timeout does not false-positive on healthy completions.
Switch the vi.mock to a partial mock using importOriginal so
spawnSync remains the real implementation for live tests while
execFileSync stays mocked for the DI-based option assertions.
* refactor(scripts): inject duplicate closure GitHub runner
* fix(scripts): declare trusted tooling pin validator
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* 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