Follow-up to #111393: run-tsgo, check-tsgo-core-boundary, profile-tsgo,
and the format-docs oxfmt shim still resolved node_modules/.bin from the
current checkout only, so dependency-less linked worktrees died with
ENOENT before oxlint's boundary prep could run. All four now use
resolveRepoToolBinPath, completing local lint/typecheck tooling for
linked worktrees.
* 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(ui): live agent activity subtitles on running sidebar sessions
* refactor(ui): lazy-load narration controller off the startup chunk, unexport test-only symbols
* chore(ui): raise startup JS budget to 312 KiB for the lazy narration feature
* test(ui): restore real timers after narration controller cases
* feat(browser): send pages to the main session from the Chrome extension
One-click page share in the OpenClaw Chrome extension: toolbar popup with an
optional note, page/selection context menu, and Alt+Shift+S. Capture is
selection-first with a readability heuristic, X/Twitter thread extraction, and
Google Docs plain-text export via the user's session cookies. Payloads ride the
existing paired relay WebSocket as a new pageShare message; the gateway-only
page-share sink wraps page text in the external-content safety boundary, then
enqueues a main-session system event and requests an immediate heartbeat
(hooks/wake semantics). Node-hosted relays report a clear unsupported error.
Capture heuristics adapted from Nat Eliason's MIT-licensed send-to-openclaw.
Co-authored-by: Codex <codex@openai.com>
* fix(browser): keep page-controlled metadata inside the share safety boundary
Review findings: move title/URL inside wrapExternalContent (a hostile <title>
must not become trusted header text), prefer the user's selection over the
full Google Docs export, and pass the context-menu selectionText through so
iframe selections and selections cleared during relay reconnect still win.
* fix(browser): bind context-menu shares to the click-time document
Selection shares from the context menu now send the click snapshot directly
(no recapture), so navigations during relay reconnect cannot mislabel the
source and iframe selections are preserved. The Google Docs selection probe
scans all accessible frames before falling back to the full-document export.
* test(browser): expect the page-share handler in relay server args
* fix(browser): probe only the main frame for Google Docs selections
All-frame injection rejects wholesale when one frame is inaccessible and
returns child frames in nondeterministic order, so the probe now reads the
main frame only. Child-frame selections still share correctly through the
context menu's click-time selectionText; toolbar/shortcut entry sends the
full page for that case (named tradeoff in the code comment).
* fix(browser): satisfy page-share CI gates
---------
Co-authored-by: Codex <codex@openai.com>
* feat(codex): fork upstream-linked sessions at a message via thread/fork
* fix(gateway): fail closed for rewind and branch switch on upstream-linked sessions
* fix(codex): fail closed on first-message forks, image-only prompts, and orphan archival
* fix(codex): baseline retained history and reject paginated threads on upstream fork
* fix(codex): validate the full fork prefix and fail closed across crash windows
* fix(codex): treat all non-text inputs as unverifiable in fork drift checks
* fix(codex): support first-message forks as empty-history upstream cuts
* fix(codex): reject source-id reuse and unverifiable hidden inputs in fork boundaries
* refactor(codex): materialize upstream forks from verified thread read-back
* fix(codex): satisfy strict type lanes and knip for upstream fork
* 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