Repository navigation
fix: propagate focused pane moves to attached clients - #4159
Conversation
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (4)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthrough
Suggested reviewers: Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to The focused pane-move behavior and guarded no-effect cases have targeted coverage, with no actionable risk remaining. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
|
✅ Action performedReview finished.
|
|
|
Regarding the docstring-coverage advisory: I am keeping the private test helpers and descriptive regression-test names as-is rather than adding comments that repeat their names and assertions. This change adds no public API, and the request handler and unchanged-move focus guard already explain the relevant behavior. |
refs herdrdev#4153 Root cause: A no-focus pane move can remove the source tab without changing the server's numeric workspace and tab coordinates. PaneMove is not classified as a topology change, so a client viewing that tab retains its deleted ID. Its target lookup falls back to the server default and can follow a later, unrelated focus change instead of staying in the source workspace. Fix: Include PaneMove in the existing client-location reconciliation path. Extend the existing focus-guard test with a client on the removed tab and verify that a later server-default change cannot move its view. Preserve the other client's view and existing error, no-op and zoom assertions. Validation: The minimized regression fails five times on PR herdrdev#4159's unchanged head and passes five times with the one-line fix. All 19 pane-move tests, native lint, maintenance, architecture and integration-asset checks pass. Full native just ci on the publication branch, including the unchanged prerequisites from herdrdev#4159 and herdrdev#4168, passes 3585 Rust tests with six default skips plus lint, 106 maintenance, six architecture and 39 integration-asset tests. All seven docs contract tests pass. Local Windows cross-lint was not run because its SDK is unavailable. No geometry policy, protocol or periodic rendering behavior is changed.
refs #4153 Root cause: A no-focus pane move can remove the source tab without changing the server's numeric workspace and tab coordinates. PaneMove is not classified as a topology change, so a client viewing that tab retains its deleted ID. Its target lookup falls back to the server default and can follow a later, unrelated focus change instead of staying in the source workspace. Fix: Include PaneMove in the existing client-location reconciliation path. Extend the existing focus-guard test with a client on the removed tab and verify that a later server-default change cannot move its view. Preserve the other client's view and existing error, no-op and zoom assertions. Validation: The minimized regression fails five times on PR #4159's unchanged head and passes five times with the one-line fix. All 19 pane-move tests, native lint, maintenance, architecture and integration-asset checks pass. Full native just ci on the publication branch, including the unchanged prerequisites from #4159 and #4168, passes 3585 Rust tests with six default skips plus lint, 106 maintenance, six architecture and 39 integration-asset tests. All seven docs contract tests pass. Local Windows cross-lint was not run because its SDK is unavailable. No geometry policy, protocol or periodic rendering behavior is changed.
* fix: disconnect stalled terminal observers (herdrdev#4039) * fix: order ui accent before nested config tables (herdrdev#2698) * fix: order ui accent before nested config tables refs herdrdev#2697 * test: reject duplicate ui accent examples refs herdrdev#2697 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: decouple pixel mouse from pane graphics (herdrdev#4045) refs herdrdev#3295 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: restrict claude session hooks to native sources (herdrdev#4046) refs herdrdev#4018 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(input): preserve super-modified pane keys (herdrdev#4044) refs herdrdev#3710 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: relax windows docs snapshot timeout (herdrdev#4049) Windows CI runs these documentation contract fixtures under variable load. A degraded runner pushed the preview and versions snapshot integration tests past their 30s ceiling, while a healthy run finishes in seconds. Keep 30s elsewhere and give Windows a 120s ceiling. * fix: retain validated split mouse reports across idle gaps (herdrdev#4048) refs herdrdev#3911 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(server): transfer handoff descriptors in batches (herdrdev#3411) Live handoff refused any session with more than 64 panes. The pane count was checked twice against MAX_FDS_PER_HANDOFF, and the transfer itself put every pane's pty master into one SCM_RIGHTS control message, so the guard was the only thing keeping the send inside the kernel's per-message limit. A session past the limit could only be updated by closing panes or by a normal restart, which ends every pane process. Send the descriptors in batches of 64 instead and drop both guards. The receiving side accumulates across recvmsg calls until the expected count arrives, bounds every SCM_RIGHTS payload it reads by the control bytes the kernel returned, rejects a batch that carries more descriptors than it asked for, and closes the descriptors it already holds on any failure. A session of 64 panes or fewer still produces one batch, so the bytes on the wire are unchanged and HANDOFF_VERSION stays at 1. refs herdrdev#3393 Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: deflake windows metadata ttl expiry assertion (herdrdev#4050) The clear-only metadata test used a 1 ms ttl, so on loaded Windows runners set_agent_metadata could see the entry as expired, drop it, and recreate it with a None ttl before next_agent_metadata_expiry() ran. Expiry is forced explicitly later in the test, so use a long ttl to keep the no-extension assertion independent of wall-clock scheduling. * fix: detect kimi npm shim on windows (herdrdev#4053) refs herdrdev#3317 * fix(remote): resolve ssh host aliases from git bash on windows (herdrdev#4054) Windows OpenSSH resolves drive-letter Include paths, but Git for Windows' MSYS OpenSSH does not, so the managed remote SSH config silently ignored the user's ~/.ssh/config when herdr ran under Git Bash. Include the user config through ~/.ssh/config, which both implementations expand to the same file. refs herdrdev#3947 * feat: add Letta Code detection and restore (herdrdev#3107) refs herdrdev#3106 Co-authored-by: Cameron <cameron@pfiffer.org> * fix(windows): disable host mouse reporting on terminal restore (herdrdev#4055) Windows skipped the xterm mouse-reporting reset because clear_host_mouse_reporting was a no-op there. Standalone Git Bash (mintty) keeps SGR mouse reporting enabled after detach, so mouse motion leaks escape sequences into the shell. Emit the reset sequence on Windows too and include the X10 mode. refs herdrdev#3748 * fix: keep hermes enabled list valid yaml (herdrdev#4056) * fix: keep hermes enabled list valid yaml refs herdrdev#3839 * fix: preserve hermes inline list quoting and comments refs herdrdev#3839 * fix(ui): keep muted sidebar and tab labels readable (herdrdev#4062) refs herdrdev#2692 * fix(windows): honor default_shell in login shell mode (herdrdev#4060) * fix(windows): honor default_shell in login shell mode Login-mode panes used portable-pty's default-program builder, which resolves to %ComSpec% (cmd.exe) on Windows and ignores the SHELL env override, so [terminal].default_shell was silently ignored. Build the launch command from the configured shell instead and append the shell's own login flag for POSIX-style shells that support one. refs herdrdev#1445 * fix(windows): add csh and tcsh to login shell flags refs herdrdev#1445 * fix(windows): keep powershell cwd reporting in login mode (herdrdev#4065) Windows login-mode PowerShell panes are now launched directly, but the PowerShell prompt-based cwd integration was still gated to non-login mode, leaving those panes without repository-owned cwd reporting after Set-Location. Enable the existing integration for login-mode PowerShell and build the integration args on the shared direct-shell path. Follow-up to herdrdev#4060. * fix: keep an agent name through a process-exit observation (herdrdev#3574) A process-exit observation is not proof the agent is gone: the same observation can be wrong while the agent keeps running, and the name is the only handle its owner has on the pane. Detection uncertainty already preserves the name, so releasing it on the observation alone contradicts that contract and leaves a live agent unreachable by name with a rename as the only recovery. Free the name at the point the agent actually leaves the pane - a recorded exit with no agent detected any more - so a wrong observation costs nothing and a real exit still releases the name for reuse. refs herdrdev#3225 Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: auto-expire worktree action error banners (herdrdev#4063) * fix: auto-expire worktree action error banners refs herdrdev#1827 * fix: restart endpoint error timeout on repeated errors Route every transient client-side action error through a setter that installs a fresh deadline, so an identical repeated message cannot inherit an older, nearly-expired deadline. refs herdrdev#1827 * test: derive endpoint error expiry assertions from stored deadline refs herdrdev#1827 * test: deflake federated local input race (herdrdev#4066) refs herdrdev#3670 * fix: recognize kitty CSI-u codepoints for F1 through F12 (herdrdev#2378) Codepoints 57364-57375 (F1-F12) were missing from the kitty-protocol codepoint table, so terminals that send function keys as full CSI-u sequences (e.g. Ghostty, which enables the kitty keyboard protocol by default) had those keys silently dropped. Confirmed live against a running herdr session: injecting the raw F3/F4 codepoint sequences did nothing before this fix and correctly triggered previous_tab/next_tab after. refs herdrdev#1809 Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(input): accept parameterized CSI-tilde F1-F4 keys (herdrdev#4067) Terminals such as foot send F1-F4 as parameterized CSI-tilde sequences (for example F3 as `\x1b[13;1:1~`). `parse_xterm_modified_special_sequence` mapped codes 15-24 but not 11-14, so these presses were dropped before keybindings saw them. Map 11-14 to F1-F4, matching the unmodified `\x1b[11~`..`\x1b[14~` aliases already accepted. refs herdrdev#1809 * fix: clean up failed terminal observer setup (herdrdev#4071) refs herdrdev#3612 * chore: add jjliebig as approved contributor and maintainer * feat: install windows remote host packages (herdrdev#3687) * feat: install windows remote host packages refs herdrdev#3651 * docs: clarify windows saved reconnect package behavior refs herdrdev#3651 * fix: include windows installer in nix sources refs herdrdev#3651 * fix: preserve paths in windows scp transfers refs herdrdev#3651 * refactor: isolate windows remote package setup refs herdrdev#3651 * feat: add ctrl-hover highlighting and fix wrapped links (herdrdev#4074) refs herdrdev#1282 * fix(linux): bound the foreground process-tree scan (herdrdev#3674) refs herdrdev#3621 Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: keep plugins working after client-only updates (herdrdev#4077) * ci: migrate remaining pinned actions to node24 (herdrdev#4023) * ci: migrate remaining pinned actions to node24 actions/cache@v4 and mlugg/setup-zig@v2.2.1 still target Node 20, which GitHub removes from runners on 2026-09-23. Bump actions/cache to v6.1.0 and replace mlugg/setup-zig with vercel-labs/setup-zig@v1.0.2. vercel-labs/setup-zig takes only `version`, so the use-cache/cache-size-limit tuning and Zig global-cache reuse are dropped. See the PR for alternatives. * ci: restore zig caching for vercel setup-zig vercel-labs/setup-zig has no cache controls, so redirect the Zig global cache into the workspace .zig-cache and run the Zig build cache step on Linux too. Drop this commit if we switch to a setup action that owns Zig caching, e.g. step-security/setup-zig or xyzzylabs/setup-zig. * fix: clarify agent integration settings description * fix: scope agent views to the selected machine (herdrdev#3784) * fix: scope agent views to the selected machine refs herdrdev#3732 * fix: preserve stable agent view ordering refs herdrdev#3732 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(windows): restore mouse reporting when reattaching (herdrdev#4080) * fix(client): re-assert host mouse modes after a terminal reconnect (herdrdev#4079) Co-authored-by: dengos <dengoswei@gmail.com> * test(windows): deflake media player invalid media assertion (herdrdev#4082) * test(windows): deflake media player invalid media assertion The Windows sound test required MediaPlayer's MediaFailed event for invalid media. Some runners never raise it, so the script exits through its playback timer and the test saw "sound playback timed out" instead, panicking on the MediaFailed assertion. Accept either terminal error and keep the timer short under test via HERDR_SOUND_TIMEOUT_SECONDS so a missed MediaFailed no longer waits out the production 15 second bound. * test(windows): keep media player timeout override test-only Generate the short-timer player script only in tests instead of reading a process environment override. This keeps the production script's fixed 15 second bound unchanged, and the script-content test now proves the test variant rewrites the timer. * fix: hide only kitty image placements overlapping overlays (herdrdev#4081) * fix: clean up idle ssh bridges and back off flapping connections (herdrdev#4083) * fix: clean up idle ssh bridges and back off flapping connections * chore: keep ssh liveness smoke tooling local * fix: preserve agent panel scroll when switching machines (herdrdev#4101) refs herdrdev#3937 Root cause: Activating another endpoint applies a snapshot with a different endpoint-qualified graphics scope. This triggers reset_endpoint_projection, which clears agent_scroll together with endpoint-local state even though the agent list aggregates all machines. The selected agent receives focus, but the list jumps back to the top. Fix: Save the client-owned agent scroll offset and restore it only when activation changes endpoints. Keep same-endpoint reboot resets, workspace/tab scroll resets, input cleanup and existing list bounds clamping unchanged. This adds constant-time work on endpoint activation, with no protocol, persisted-state or pane-scaled rendering changes. Validation: The new regression fails on the original implementation with scroll 0 instead of 6. Tests cover repeated bidirectional agent clicks, stable visible rows, shrinking lists and same-machine reboots. All 242 client shell tests pass. Real SSH A/B testing against the same two disposable 0.9.0 servers reproduces the reset with both the stable and current-master clients. The patched client preserves the visible rows through six cross-machine switches and two same-machine control clicks. Native just ci passes: 3535 Rust tests, six default skips, plus lint, maintenance, architecture and integration-asset checks. All seven docs contract tests pass. Windows cross-lint was not run because its SDK is not installed; native macOS and Windows UI behavior was not tested. * fix: remove stale maintainer username * fix: detect codex activity with static titles and queued inputs (herdrdev#4099) * fix: detect codex activity with static titles and queued inputs refs herdrdev#4092 Codex 0.154.0 can omit the activity bullet when tui.animations is false and replace the literal Working label with a task-specific label. Its terminal title can also remain static during an active turn, so the OSC spinner rule is not sufficient to identify work. The screen fallback required a bullet followed by Working in the last three non-empty lines. It therefore missed both the animation-disabled status line and dynamic labels. Queued follow-ups add a heading, input text, and an edit hint below the activity line, moving that line outside the three-line region. The lower-priority non-empty OSC title rule then reported idle while Codex still showed an active timer and interrupt control. Recognize the elapsed-time and esc-to-interrupt controls in the detection snapshot before the current composer. Allow an optional bullet, a dynamic activity label, and queued follow-ups below that line. Require that no later response, completion, or interruption marker supersedes the live activity. This excludes composer text and stale output without letting an earlier interruption suppress a subsequent active turn. Keep OSC state precedence and transcript-viewer skip behavior unchanged. Update the bundled and distributed manifests together to 2026.09.14.1. Add regression coverage for optional bullets, dynamic labels, queued inputs, resumed work after interruption, and stale or quoted controls. Avoid discarding Regex search caches on every detection poll. The loader previously cloned the entire compiled rule vector, and each Regex clone created a fresh search cache pool despite sharing compiled read-only state. Share the immutable rules through Arc instead. Reload publishes a new array while in-flight readers retain a valid old generation, without holding the global cache lock during matching. Add a cheap contains gate for the required interrupt text before running the multiline expression. Add a deterministic regression for compiled-rule sharing, reload replacement, retained old readers, and concurrent detection. Measure the baseline and optimized detector kernels in three alternating rounds on CPU 24 with NUMA node 0 and CPU 25 with NUMA node 1. Real queued detection falls from about 116 to 22 microseconds, and two evaluations per pane over 15 panes fall from about 3.45 to 0.65 milliseconds on both sockets, about 81 percent less time. No benchmark match results change. The cache improvement requires the updated binary, not just a manifest reload. Validate on the official Herdr 0.9.0 release with Codex 0.154.0 by toggling the old and new manifests during the same active queued task. Confirm working for active turns and idle after completion or interruption. Pass 86 captured-screen and semantic checks through the optimized source binary, 69 manifest/cache tests, CPU-pinned render scaling, and the full native CI suite: 3535 Rust tests passed, with six default skips. Run CI serially with inherited Herdr session context removed; no tests were excluded or expectations relaxed to make CI pass. * fix: detect codex pending follow-ups as working refs herdrdev#4092 * fix: handle wrapped codex follow-up hints refs herdrdev#4092 --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> * fix: detect pi working border (herdrdev#4113) refs herdrdev#3629 * ci: restore zig local build cache reuse (herdrdev#4118) * fix: default pane splits to the calling pane (herdrdev#4123) * fix: restore saved machine screens after reconnect (herdrdev#4126) refs herdrdev#3842 * fix: scope window titles to each client view (herdrdev#4130) refs herdrdev#4091 * fix(windows): avoid lossy native Win32 input mode (herdrdev#4133) refs herdrdev#3932 refs herdrdev#3702 refs herdrdev#3910 * fix: keep modified enter compatible with legacy panes (herdrdev#4110) * fix: keep modified enter compatible with legacy panes refs herdrdev#4103 * fix: synchronize modified key negotiation state refs herdrdev#4103 * test: cover windows legacy modified enter refs herdrdev#4103 --------- Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com> * fix: avoid resending terminal cells for metadata updates (herdrdev#4137) * feat: add cursor-based editing to text inputs (herdrdev#3698) * feat: add cursor-based editing to text inputs refs herdrdev#1803 * fix: preserve dialog actions with associated text refs herdrdev#1803 * refactor: simplify text editor outcomes and tests refs herdrdev#1803 --------- Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: protect integration configs from failed writes (herdrdev#3973) * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: map pixel mouse by integer cell pitch (herdrdev#4140) refs herdrdev#3295 * fix: reject stale pane surfaces after reconnect (herdrdev#4141) * test: deflake recent CI failures (herdrdev#4139) * fix(windows): batch input trace diagnostics (herdrdev#4146) refs herdrdev#3932 * fix(windows): prefer VT input for SSH clients (herdrdev#4148) refs herdrdev#3932 * fix: reveal the full last tab in overflowing tab strips (herdrdev#4152) refs herdrdev#4151 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: reveal newly focused workspaces in the machines sidebar * fix: propagate focused pane moves to attached clients (herdrdev#4159) refs herdrdev#4153 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: wait for runtime readiness in integration tests (herdrdev#4168) Root cause: The handoff test starts an agent immediately after workspace creation, before the new shell is necessarily ready. The reconnect test assumes a fixed sidebar row and searches raw ANSI output for text that can arrive as separate incremental cell updates. Fix: Wait for a shell builtin to write a readiness marker before sending the single raw agent.start request. Exercise the gap with a delayed shell. Locate the recovered workspace in the rendered sidebar and check the reconstructed screen for selection and input, including overlay borders. Do not extend existing timeouts or add retries that hide failures. Validation: All 25 affected group and helper tests pass. Ten repeated rounds of the two runtime tests and the row helper pass, for 30 test executions without retries. Standalone native just ci passes 3582 Rust tests with six default skips, formatting, all-target Clippy, 106 maintenance tests, six architecture tests and 39 integration-asset tests. Windows cross-lint was not run because its SDK is not installed. Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: reconcile client locations after no-focus pane moves (herdrdev#4171) refs herdrdev#4153 Root cause: A no-focus pane move can remove the source tab without changing the server's numeric workspace and tab coordinates. PaneMove is not classified as a topology change, so a client viewing that tab retains its deleted ID. Its target lookup falls back to the server default and can follow a later, unrelated focus change instead of staying in the source workspace. Fix: Include PaneMove in the existing client-location reconciliation path. Extend the existing focus-guard test with a client on the removed tab and verify that a later server-default change cannot move its view. Preserve the other client's view and existing error, no-op and zoom assertions. Validation: The minimized regression fails five times on PR herdrdev#4159's unchanged head and passes five times with the one-line fix. All 19 pane-move tests, native lint, maintenance, architecture and integration-asset checks pass. Full native just ci on the publication branch, including the unchanged prerequisites from herdrdev#4159 and herdrdev#4168, passes 3585 Rust tests with six default skips plus lint, 106 maintenance, six architecture and 39 integration-asset tests. All seven docs contract tests pass. Local Windows cross-lint was not run because its SDK is unavailable. No geometry policy, protocol or periodic rendering behavior is changed. * fix: preserve live selections during pane output (herdrdev#4193) refs herdrdev#3841 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix(windows): preserve sgr mouse encoding after native capture (herdrdev#4195) refs herdrdev#4155 * fix(windows): normalize pane launch cwd casing (herdrdev#4199) * fix(windows): reject dynamic msvc runtime in packaging (herdrdev#4200) The v0.8.2 Windows archive shipped an herdr.exe that imported VCRUNTIME140.dll and the api-ms-win-crt-* Universal CRT, none of which the archive provides. Static CRT linkage was restored for x86_64-pc-windows-msvc in .cargo/config.toml, but nothing verified the packaged executable, so the dependency could silently regress. Parse the PE import and delay-import tables and reject herdr.exe when it depends on the dynamic Microsoft C/C++ runtime, including release and debug variants. Check the input while staging and re-check the staged executable before archiving so a direct archive cannot bypass the guard. refs herdrdev#3129 * fix: preserve unloaded sessions before replacement (herdrdev#4190) refs herdrdev#4125 * fix: preserve macos services across logout (herdrdev#4202) refs herdrdev#4100 * chore: approve vinayshah1998 as contributor * fix: keep remote surface updates incremental (herdrdev#4209) refs herdrdev#3769 refs herdrdev#3745 * fix: keep local usable when remote machines stall (herdrdev#4234) refs herdrdev#3903 * fix: let local agent clicks cancel remote handoffs (herdrdev#4236) refs herdrdev#3903 * fix: require exact session names before deletion (herdrdev#4233) * fix: require exact session names before deletion refs herdrdev#3819 * test: isolate session deletion regression tests refs herdrdev#3819 --------- Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: wait for app readiness before late session injection (herdrdev#4224) Root cause: The API socket is bound before App evaluates session restore. Waiting only for that socket lets the late-session test inject its file during startup, so the server may load it normally instead of exercising late-file recovery. The missing backup then fails an otherwise correct persistence assertion. Fix: Wait for a read-only workspace-list request to pass through App and assert that the workspace list is still empty before injecting the session file. Keep the existing autosave deadline and byte-for-byte backup assertion. Add no sleeps, retries, longer timeouts or production changes. Validation: The original assertion fails with unchanged master. With the barrier, five independent runs pass. Full native just ci passes 3624 Rust tests with six default skips, formatting, all-target Clippy, 112 maintenance tests, six architecture tests and 39 integration-asset tests. All seven docs contract tests pass. Local Windows cross-lint was not run because its SDK is absent. Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: offer forced removal for worktrees with submodules (herdrdev#4238) refs herdrdev#1797 * ci: promote stable releases from protected previews (herdrdev#4241) * fix: address release promotion review findings * fix: avoid workstation sdk requirement in preview checks * docs: publish preview documentation * release: synchronize metadata for v0.9.1 * fix: respect opencode tui.json registrations (herdrdev#4250) refs herdrdev#4240 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: cache saved machine connection metadata (herdrdev#4256) refs herdrdev#4252 * fix(windows): preserve VT input during mouse refresh (herdrdev#4257) refs herdrdev#4251 * fix(windows): restore terminal after setup errors (herdrdev#4261) refs herdrdev#4251 * fix(windows): recover VT input without changing readers (herdrdev#4269) refs herdrdev#4251 * fix: address review comments on #55 --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> Co-authored-by: akbash <akbash@herdr.dev> Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> Co-authored-by: Kataoka Katsuki <49934462+kataokatsuki@users.noreply.github.com> Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com> Co-authored-by: Cameron <cameron@pfiffer.org> Co-authored-by: caner-akca <94343893+caner-akca@users.noreply.github.com> Co-authored-by: Kakumanu Ashok Reddy <kakumanuashokreddyias2024@gmail.com> Co-authored-by: dengos <dengoswei@gmail.com> Co-authored-by: Eric Yue <jiacheng.yue@foxmail.com> Co-authored-by: Mark Jaquith <mark@jaquith.me> Co-authored-by: kangal-bot <285672167+kangal-bot@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: zuraffa-review[bot] <326983185+zuraffa-review[bot]@users.noreply.github.com>
Absorbs upstream through v0.9.1 release metadata. Notable fixes: stable release promotion from protected previews (herdrdev#4241), windows VT input recovery and pwsh default shell (herdrdev#4269, herdrdev#4297), cursor-based text input editing (herdrdev#3698), ctrl-hover highlighting and wrapped links (herdrdev#4074), whole-word selection during drag (herdrdev#3969), client location reconciliation after pane moves (herdrdev#4171, herdrdev#4159), saved machine screen restore after reconnect (herdrdev#4126), overflowing tab strips reveal the full last tab (herdrdev#4152), muted sidebar/tab label readability (herdrdev#4062), machine connection metadata caching (herdrdev#4256), unloaded session preservation (herdrdev#4190, herdrdev#4125), kitty CSI-u/CSI-tilde F-keys (herdrdev#2378, herdrdev#4067), pixel mouse integer cell pitch (herdrdev#4140), ssh bridge cleanup and backoff (herdrdev#4083), Letta Code detection (herdrdev#3107). Conflicts resolved per FORK-CHANGELOG.md: justfile kept upstream's test_release/test_windows_input additions plus the fork's test_fork_changelog_check; tabs.rs took upstream's max_tab_scroll rework and muted tab-label style, keeping the vertical tab strip intact.
* docs: publish release distribution for v0.9.0 * fix: reduce idle ssh cpu without dropping final output (herdrdev#3728) * fix: stop polling idle ssh uploads on unix * fix: preserve ssh output during idle bridge cancellation * test: exercise active ssh upload cancellation * docs: publish preview documentation * fix: show ssh errors during machine setup (herdrdev#3733) refs herdrdev#3731 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * chore: move marketplace worker to website repository * fix: collapse worktree groups with saved machines (herdrdev#3781) * fix: collapse worktree groups with saved machines refs herdrdev#3778 * fix: scope worktree collapse by machine refs herdrdev#3778 * fix: collapse machines without switching focus refs herdrdev#3778 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> * fix: support preinstalled windows remote hosts (herdrdev#3661) * fix: support windows hosts in saved ssh machines refs herdrdev#3651 * fix: keep windows ssh bridge writes blocking refs herdrdev#3651 * fix: close saved windows ssh bootstrap input refs herdrdev#3651 * refactor: simplify remote bridge helpers refs herdrdev#3651 * refactor: isolate remote bridge platform behavior refs herdrdev#3651 * fix: close captured ssh input under windows consoles refs herdrdev#3651 * test: remove redundant remote command assertions refs herdrdev#3651 refs herdrdev#3701 * test: handle framed ssh commands in machine setup refs herdrdev#3651 --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> * fix: restore autofocus when creating worktrees (herdrdev#3787) * fix: restore autofocus when creating worktrees refs herdrdev#3766 * fix: let the client focus newly created worktrees refs herdrdev#3766 * feat: navigate and highlight workspaces across machines (herdrdev#3755) * fix: highlight workspace navigation with saved machines refs herdrdev#3754 * feat: navigate workspaces across connected machines refs herdrdev#3754 * feat: navigate workspaces across machines refs herdrdev#3754 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> * fix: avoid full-screen copies for retained client updates (herdrdev#3837) refs herdrdev#3822 * fix: restore plugin focus events for client navigation (herdrdev#3850) refs herdrdev#3824 * fix: retain double-click selections and make tests root-safe (herdrdev#3854) refs herdrdev#3847 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: project public agent focus to attached clients (herdrdev#3764) refs herdrdev#3760 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: restore connected session navigator tree glyphs (herdrdev#3861) refs herdrdev#3843 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix(pi): support Windows session paths (herdrdev#3729) refs herdrdev#3726 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: stop token selection at CJK punctuation (herdrdev#3708) refs herdrdev#3706 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: keep spaces sidebar stable during resize (herdrdev#3862) refs herdrdev#3817 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: accept session names after option terminator (herdrdev#3222) refs herdrdev#3220 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix(linux): identify Herdr desktop notifications (herdrdev#3639) refs herdrdev#3638 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix(windows): preserve legacy mouse reports over ssh (herdrdev#3742) * fix(windows): preserve legacy mouse reports over ssh refs herdrdev#3735 * fix(windows): preserve input after missing mouse release refs herdrdev#3735 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: use host background for pane selection highlights (herdrdev#3894) refs herdrdev#3798 * fix: preserve focused viewer geometry during activation (herdrdev#3746) * fix: preserve focused viewer geometry during activation refs herdrdev#3744 * fix: preserve geometry through activation sync refs herdrdev#3744 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix(input): accept WezTerm control key reports (herdrdev#3592) * fix(input): accept WezTerm control key reports refs herdrdev#3589 * fix(input): preserve WezTerm tab and escape reports refs herdrdev#3589 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: accept machine add options before the SSH target (herdrdev#3897) refs herdrdev#3883 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: bound mouse selection repaint cadence (herdrdev#3901) refs herdrdev#3895 * chore: upgrade libghostty and preserve terminal behavior (herdrdev#3906) * feat: route cli commands to saved ssh machines (herdrdev#3918) * feat: support conditional sidebar token hiding (herdrdev#3925) * fix: keep terminal snapshots consistent while scrolling (herdrdev#3928) refs herdrdev#3900 * fix: explain zig version requirement on build failure (herdrdev#3931) * fix: preserve plugin registry symlinks (herdrdev#3927) refs herdrdev#3922 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * ci: trigger nix updates after stable releases * chore: add one-time windows cross-check setup * test: make windows cross-check path assertions portable (herdrdev#3954) * fix: recognize claude unicode spinner during active turns (herdrdev#3953) refs herdrdev#3949 * fix: preserve local direct kitty graphics transport (herdrdev#3957) refs herdrdev#3785 * fix: detect cline launchers and idle states (herdrdev#3959) refs herdrdev#2396 * fix: flush Windows Codex paste burst before prompt submit (herdrdev#3961) Codex's Windows input reader does not surface bracketed paste. It buffers the prompt as a paste burst and rewrites a following Enter into a newline until the burst idles out, so the previous size-based delay only reduced the chance of losing the submit. Append a non-character key after the paste so Codex flushes the burst synchronously, giving every prompt a deterministic submit boundary. refs herdrdev#3187 * fix: lint all targets on windows (herdrdev#3963) Native Windows lint and check only clippy-checked the herdr binary, while macOS and Linux run clippy across all targets. Enable --all-targets on Windows and gate the Unix-only test helpers that this exposed so Windows check matches the Unix one. * fix(config): keep utf8 bom from breaking config reads and writes (herdrdev#3962) * fix(config): keep utf8 bom from breaking config reads and writes refs herdrdev#3678 * fix(config): only strip displaced boms when config is invalid refs herdrdev#3678 * fix(config): strip only boms the toml parser rejects refs herdrdev#3678 * fix(remote): show SSH authentication output (herdrdev#3609) * fix(remote): show SSH authentication output refs herdrdev#3606 * test(remote): stabilize SSH authentication output regression refs herdrdev#3606 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * ci: reuse compiler state across validation runs (herdrdev#3956) * ci: retain zig build caches across validation runs * ci: reduce debug information in validation builds * ci: build only the ghostty test target for scalar checks * ci: measure debug profiles and remove duplicate macos caching * ci: compare scalar build scopes on one runner * ci: keep the zig compiler path stable across runs * ci: run macos and windows lint alongside tests * ci: inspect remaining zig cache misses * ci: remove completed measurement probes * ci: compare windows linkers without changing release builds * ci: test linux incremental reuse and correct the linker probe * ci: measure windows incremental state reuse * test: verify incremental reuse after a source change * ci: finalize measured compiler cache optimizations * fix: preserve whole-word selection while dragging (herdrdev#3969) refs herdrdev#1836 * docs: enable nesting in throwaway repro setup * fix: opencode v2 lifecycle reporting (herdrdev#3757) * fix: support opencode v2 lifecycle reporting * fix: repair Japanese docs * fix: ignore payload-less OpenCode events * fix: harden opencode v2 integration install and reporting Follow-ups on top of the V2 lifecycle support: - create `cli.json` when OpenCode has no V1 TUI preferences (`tui.json` or `kv.json`) to migrate, instead of only registering into an existing file - settle stalled socket attempts with a plain connect timer, and resend the latest lifecycle state after a failed delivery so the pane cannot get stuck - resolve the OpenCode state directory from `XDG_STATE_HOME` - remove the managed `herdr-opencode` directory on uninstall - document failed executions reporting `blocked`, keep the integration test environment independent of an inherited `XDG_STATE_HOME`, and simplify `reconcileBlockers` to reassign its map --------- Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com> * fix: suppress Windows dead-key fallback characters in remote panes (herdrdev#3972) refs herdrdev#3948 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: preserve complete alternate-screen history reads (herdrdev#3979) * fix: ignore nested OMP session reports (herdrdev#3994) refs herdrdev#2593 * fix: finish windows ssh setup after bridge exit (herdrdev#3719) refs herdrdev#3651 * fix: keep windows endpoint writes progressing (herdrdev#3721) refs herdrdev#3651 * fix: batch queued endpoint input frames (herdrdev#4004) refs herdrdev#3833 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: allow retrying partial worktree removal (herdrdev#3315) refs herdrdev#3314 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: recognize codex composer sparkles (herdrdev#4015) refs herdrdev#3988 * fix: honor space row gaps across machines (herdrdev#3740) * fix: honor space row gaps across machines refs herdrdev#3738 * test: cover scrolling with machine space gaps refs herdrdev#3738 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: restore alternate-screen pane width (herdrdev#4016) refs herdrdev#3329 * fix: update grok session after new (herdrdev#2683) * fix: update grok session after new refs herdrdev#2681 * fix: avoid powershell args shadowing refs herdrdev#2681 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: deflake windows endpoint flush drain test (herdrdev#4021) The polling peer drains at production's 2ms Windows read cadence, so a 1 MiB frame took ~5.5s locally and exceeded the test's 10s flush deadline under CI load. Use a 256 KiB frame, still well above the 64 KiB batch limit, and a 30s deadline so the assertion no longer depends on CI scheduling. * fix(linux): avoid blocking proc reads for wsl agents (herdrdev#2179) Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: disconnect stalled terminal observers (herdrdev#4039) * fix: order ui accent before nested config tables (herdrdev#2698) * fix: order ui accent before nested config tables refs herdrdev#2697 * test: reject duplicate ui accent examples refs herdrdev#2697 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: decouple pixel mouse from pane graphics (herdrdev#4045) refs herdrdev#3295 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: restrict claude session hooks to native sources (herdrdev#4046) refs herdrdev#4018 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(input): preserve super-modified pane keys (herdrdev#4044) refs herdrdev#3710 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: relax windows docs snapshot timeout (herdrdev#4049) Windows CI runs these documentation contract fixtures under variable load. A degraded runner pushed the preview and versions snapshot integration tests past their 30s ceiling, while a healthy run finishes in seconds. Keep 30s elsewhere and give Windows a 120s ceiling. * fix: retain validated split mouse reports across idle gaps (herdrdev#4048) refs herdrdev#3911 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(server): transfer handoff descriptors in batches (herdrdev#3411) Live handoff refused any session with more than 64 panes. The pane count was checked twice against MAX_FDS_PER_HANDOFF, and the transfer itself put every pane's pty master into one SCM_RIGHTS control message, so the guard was the only thing keeping the send inside the kernel's per-message limit. A session past the limit could only be updated by closing panes or by a normal restart, which ends every pane process. Send the descriptors in batches of 64 instead and drop both guards. The receiving side accumulates across recvmsg calls until the expected count arrives, bounds every SCM_RIGHTS payload it reads by the control bytes the kernel returned, rejects a batch that carries more descriptors than it asked for, and closes the descriptors it already holds on any failure. A session of 64 panes or fewer still produces one batch, so the bytes on the wire are unchanged and HANDOFF_VERSION stays at 1. refs herdrdev#3393 Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: deflake windows metadata ttl expiry assertion (herdrdev#4050) The clear-only metadata test used a 1 ms ttl, so on loaded Windows runners set_agent_metadata could see the entry as expired, drop it, and recreate it with a None ttl before next_agent_metadata_expiry() ran. Expiry is forced explicitly later in the test, so use a long ttl to keep the no-extension assertion independent of wall-clock scheduling. * fix: detect kimi npm shim on windows (herdrdev#4053) refs herdrdev#3317 * fix(remote): resolve ssh host aliases from git bash on windows (herdrdev#4054) Windows OpenSSH resolves drive-letter Include paths, but Git for Windows' MSYS OpenSSH does not, so the managed remote SSH config silently ignored the user's ~/.ssh/config when herdr ran under Git Bash. Include the user config through ~/.ssh/config, which both implementations expand to the same file. refs herdrdev#3947 * feat: add Letta Code detection and restore (herdrdev#3107) refs herdrdev#3106 Co-authored-by: Cameron <cameron@pfiffer.org> * fix(windows): disable host mouse reporting on terminal restore (herdrdev#4055) Windows skipped the xterm mouse-reporting reset because clear_host_mouse_reporting was a no-op there. Standalone Git Bash (mintty) keeps SGR mouse reporting enabled after detach, so mouse motion leaks escape sequences into the shell. Emit the reset sequence on Windows too and include the X10 mode. refs herdrdev#3748 * fix: keep hermes enabled list valid yaml (herdrdev#4056) * fix: keep hermes enabled list valid yaml refs herdrdev#3839 * fix: preserve hermes inline list quoting and comments refs herdrdev#3839 * fix(ui): keep muted sidebar and tab labels readable (herdrdev#4062) refs herdrdev#2692 * fix(windows): honor default_shell in login shell mode (herdrdev#4060) * fix(windows): honor default_shell in login shell mode Login-mode panes used portable-pty's default-program builder, which resolves to %ComSpec% (cmd.exe) on Windows and ignores the SHELL env override, so [terminal].default_shell was silently ignored. Build the launch command from the configured shell instead and append the shell's own login flag for POSIX-style shells that support one. refs herdrdev#1445 * fix(windows): add csh and tcsh to login shell flags refs herdrdev#1445 * fix(windows): keep powershell cwd reporting in login mode (herdrdev#4065) Windows login-mode PowerShell panes are now launched directly, but the PowerShell prompt-based cwd integration was still gated to non-login mode, leaving those panes without repository-owned cwd reporting after Set-Location. Enable the existing integration for login-mode PowerShell and build the integration args on the shared direct-shell path. Follow-up to herdrdev#4060. * fix: keep an agent name through a process-exit observation (herdrdev#3574) A process-exit observation is not proof the agent is gone: the same observation can be wrong while the agent keeps running, and the name is the only handle its owner has on the pane. Detection uncertainty already preserves the name, so releasing it on the observation alone contradicts that contract and leaves a live agent unreachable by name with a rename as the only recovery. Free the name at the point the agent actually leaves the pane - a recorded exit with no agent detected any more - so a wrong observation costs nothing and a real exit still releases the name for reuse. refs herdrdev#3225 Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: auto-expire worktree action error banners (herdrdev#4063) * fix: auto-expire worktree action error banners refs herdrdev#1827 * fix: restart endpoint error timeout on repeated errors Route every transient client-side action error through a setter that installs a fresh deadline, so an identical repeated message cannot inherit an older, nearly-expired deadline. refs herdrdev#1827 * test: derive endpoint error expiry assertions from stored deadline refs herdrdev#1827 * test: deflake federated local input race (herdrdev#4066) refs herdrdev#3670 * fix: recognize kitty CSI-u codepoints for F1 through F12 (herdrdev#2378) Codepoints 57364-57375 (F1-F12) were missing from the kitty-protocol codepoint table, so terminals that send function keys as full CSI-u sequences (e.g. Ghostty, which enables the kitty keyboard protocol by default) had those keys silently dropped. Confirmed live against a running herdr session: injecting the raw F3/F4 codepoint sequences did nothing before this fix and correctly triggered previous_tab/next_tab after. refs herdrdev#1809 Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(input): accept parameterized CSI-tilde F1-F4 keys (herdrdev#4067) Terminals such as foot send F1-F4 as parameterized CSI-tilde sequences (for example F3 as `\x1b[13;1:1~`). `parse_xterm_modified_special_sequence` mapped codes 15-24 but not 11-14, so these presses were dropped before keybindings saw them. Map 11-14 to F1-F4, matching the unmodified `\x1b[11~`..`\x1b[14~` aliases already accepted. refs herdrdev#1809 * fix: clean up failed terminal observer setup (herdrdev#4071) refs herdrdev#3612 * chore: add jjliebig as approved contributor and maintainer * feat: install windows remote host packages (herdrdev#3687) * feat: install windows remote host packages refs herdrdev#3651 * docs: clarify windows saved reconnect package behavior refs herdrdev#3651 * fix: include windows installer in nix sources refs herdrdev#3651 * fix: preserve paths in windows scp transfers refs herdrdev#3651 * refactor: isolate windows remote package setup refs herdrdev#3651 * feat: add ctrl-hover highlighting and fix wrapped links (herdrdev#4074) refs herdrdev#1282 * fix(linux): bound the foreground process-tree scan (herdrdev#3674) refs herdrdev#3621 Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: keep plugins working after client-only updates (herdrdev#4077) * ci: migrate remaining pinned actions to node24 (herdrdev#4023) * ci: migrate remaining pinned actions to node24 actions/cache@v4 and mlugg/setup-zig@v2.2.1 still target Node 20, which GitHub removes from runners on 2026-09-23. Bump actions/cache to v6.1.0 and replace mlugg/setup-zig with vercel-labs/setup-zig@v1.0.2. vercel-labs/setup-zig takes only `version`, so the use-cache/cache-size-limit tuning and Zig global-cache reuse are dropped. See the PR for alternatives. * ci: restore zig caching for vercel setup-zig vercel-labs/setup-zig has no cache controls, so redirect the Zig global cache into the workspace .zig-cache and run the Zig build cache step on Linux too. Drop this commit if we switch to a setup action that owns Zig caching, e.g. step-security/setup-zig or xyzzylabs/setup-zig. * fix: clarify agent integration settings description * fix: scope agent views to the selected machine (herdrdev#3784) * fix: scope agent views to the selected machine refs herdrdev#3732 * fix: preserve stable agent view ordering refs herdrdev#3732 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix(windows): restore mouse reporting when reattaching (herdrdev#4080) * fix(client): re-assert host mouse modes after a terminal reconnect (herdrdev#4079) Co-authored-by: dengos <dengoswei@gmail.com> * test(windows): deflake media player invalid media assertion (herdrdev#4082) * test(windows): deflake media player invalid media assertion The Windows sound test required MediaPlayer's MediaFailed event for invalid media. Some runners never raise it, so the script exits through its playback timer and the test saw "sound playback timed out" instead, panicking on the MediaFailed assertion. Accept either terminal error and keep the timer short under test via HERDR_SOUND_TIMEOUT_SECONDS so a missed MediaFailed no longer waits out the production 15 second bound. * test(windows): keep media player timeout override test-only Generate the short-timer player script only in tests instead of reading a process environment override. This keeps the production script's fixed 15 second bound unchanged, and the script-content test now proves the test variant rewrites the timer. * fix: hide only kitty image placements overlapping overlays (herdrdev#4081) * fix: clean up idle ssh bridges and back off flapping connections (herdrdev#4083) * fix: clean up idle ssh bridges and back off flapping connections * chore: keep ssh liveness smoke tooling local * fix: preserve agent panel scroll when switching machines (herdrdev#4101) refs herdrdev#3937 Root cause: Activating another endpoint applies a snapshot with a different endpoint-qualified graphics scope. This triggers reset_endpoint_projection, which clears agent_scroll together with endpoint-local state even though the agent list aggregates all machines. The selected agent receives focus, but the list jumps back to the top. Fix: Save the client-owned agent scroll offset and restore it only when activation changes endpoints. Keep same-endpoint reboot resets, workspace/tab scroll resets, input cleanup and existing list bounds clamping unchanged. This adds constant-time work on endpoint activation, with no protocol, persisted-state or pane-scaled rendering changes. Validation: The new regression fails on the original implementation with scroll 0 instead of 6. Tests cover repeated bidirectional agent clicks, stable visible rows, shrinking lists and same-machine reboots. All 242 client shell tests pass. Real SSH A/B testing against the same two disposable 0.9.0 servers reproduces the reset with both the stable and current-master clients. The patched client preserves the visible rows through six cross-machine switches and two same-machine control clicks. Native just ci passes: 3535 Rust tests, six default skips, plus lint, maintenance, architecture and integration-asset checks. All seven docs contract tests pass. Windows cross-lint was not run because its SDK is not installed; native macOS and Windows UI behavior was not tested. * fix: remove stale maintainer username * fix: detect codex activity with static titles and queued inputs (herdrdev#4099) * fix: detect codex activity with static titles and queued inputs refs herdrdev#4092 Codex 0.154.0 can omit the activity bullet when tui.animations is false and replace the literal Working label with a task-specific label. Its terminal title can also remain static during an active turn, so the OSC spinner rule is not sufficient to identify work. The screen fallback required a bullet followed by Working in the last three non-empty lines. It therefore missed both the animation-disabled status line and dynamic labels. Queued follow-ups add a heading, input text, and an edit hint below the activity line, moving that line outside the three-line region. The lower-priority non-empty OSC title rule then reported idle while Codex still showed an active timer and interrupt control. Recognize the elapsed-time and esc-to-interrupt controls in the detection snapshot before the current composer. Allow an optional bullet, a dynamic activity label, and queued follow-ups below that line. Require that no later response, completion, or interruption marker supersedes the live activity. This excludes composer text and stale output without letting an earlier interruption suppress a subsequent active turn. Keep OSC state precedence and transcript-viewer skip behavior unchanged. Update the bundled and distributed manifests together to 2026.09.14.1. Add regression coverage for optional bullets, dynamic labels, queued inputs, resumed work after interruption, and stale or quoted controls. Avoid discarding Regex search caches on every detection poll. The loader previously cloned the entire compiled rule vector, and each Regex clone created a fresh search cache pool despite sharing compiled read-only state. Share the immutable rules through Arc instead. Reload publishes a new array while in-flight readers retain a valid old generation, without holding the global cache lock during matching. Add a cheap contains gate for the required interrupt text before running the multiline expression. Add a deterministic regression for compiled-rule sharing, reload replacement, retained old readers, and concurrent detection. Measure the baseline and optimized detector kernels in three alternating rounds on CPU 24 with NUMA node 0 and CPU 25 with NUMA node 1. Real queued detection falls from about 116 to 22 microseconds, and two evaluations per pane over 15 panes fall from about 3.45 to 0.65 milliseconds on both sockets, about 81 percent less time. No benchmark match results change. The cache improvement requires the updated binary, not just a manifest reload. Validate on the official Herdr 0.9.0 release with Codex 0.154.0 by toggling the old and new manifests during the same active queued task. Confirm working for active turns and idle after completion or interruption. Pass 86 captured-screen and semantic checks through the optimized source binary, 69 manifest/cache tests, CPU-pinned render scaling, and the full native CI suite: 3535 Rust tests passed, with six default skips. Run CI serially with inherited Herdr session context removed; no tests were excluded or expectations relaxed to make CI pass. * fix: detect codex pending follow-ups as working refs herdrdev#4092 * fix: handle wrapped codex follow-up hints refs herdrdev#4092 --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> * fix: detect pi working border (herdrdev#4113) refs herdrdev#3629 * ci: restore zig local build cache reuse (herdrdev#4118) * fix: default pane splits to the calling pane (herdrdev#4123) * fix: restore saved machine screens after reconnect (herdrdev#4126) refs herdrdev#3842 * fix: scope window titles to each client view (herdrdev#4130) refs herdrdev#4091 * fix(windows): avoid lossy native Win32 input mode (herdrdev#4133) refs herdrdev#3932 refs herdrdev#3702 refs herdrdev#3910 * fix: keep modified enter compatible with legacy panes (herdrdev#4110) * fix: keep modified enter compatible with legacy panes refs herdrdev#4103 * fix: synchronize modified key negotiation state refs herdrdev#4103 * test: cover windows legacy modified enter refs herdrdev#4103 --------- Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com> * fix: avoid resending terminal cells for metadata updates (herdrdev#4137) * feat: add cursor-based editing to text inputs (herdrdev#3698) * feat: add cursor-based editing to text inputs refs herdrdev#1803 * fix: preserve dialog actions with associated text refs herdrdev#1803 * refactor: simplify text editor outcomes and tests refs herdrdev#1803 --------- Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: protect integration configs from failed writes (herdrdev#3973) * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 * fix: atomically write integration configs refs herdrdev#3970 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: map pixel mouse by integer cell pitch (herdrdev#4140) refs herdrdev#3295 * fix: reject stale pane surfaces after reconnect (herdrdev#4141) * test: deflake recent CI failures (herdrdev#4139) * fix(windows): batch input trace diagnostics (herdrdev#4146) refs herdrdev#3932 * fix(windows): prefer VT input for SSH clients (herdrdev#4148) refs herdrdev#3932 * fix: reveal the full last tab in overflowing tab strips (herdrdev#4152) refs herdrdev#4151 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix: reveal newly focused workspaces in the machines sidebar * fix: propagate focused pane moves to attached clients (herdrdev#4159) refs herdrdev#4153 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: wait for runtime readiness in integration tests (herdrdev#4168) Root cause: The handoff test starts an agent immediately after workspace creation, before the new shell is necessarily ready. The reconnect test assumes a fixed sidebar row and searches raw ANSI output for text that can arrive as separate incremental cell updates. Fix: Wait for a shell builtin to write a readiness marker before sending the single raw agent.start request. Exercise the gap with a delayed shell. Locate the recovered workspace in the rendered sidebar and check the reconstructed screen for selection and input, including overlay borders. Do not extend existing timeouts or add retries that hide failures. Validation: All 25 affected group and helper tests pass. Ten repeated rounds of the two runtime tests and the row helper pass, for 30 test executions without retries. Standalone native just ci passes 3582 Rust tests with six default skips, formatting, all-target Clippy, 106 maintenance tests, six architecture tests and 39 integration-asset tests. Windows cross-lint was not run because its SDK is not installed. Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: reconcile client locations after no-focus pane moves (herdrdev#4171) refs herdrdev#4153 Root cause: A no-focus pane move can remove the source tab without changing the server's numeric workspace and tab coordinates. PaneMove is not classified as a topology change, so a client viewing that tab retains its deleted ID. Its target lookup falls back to the server default and can follow a later, unrelated focus change instead of staying in the source workspace. Fix: Include PaneMove in the existing client-location reconciliation path. Extend the existing focus-guard test with a client on the removed tab and verify that a later server-default change cannot move its view. Preserve the other client's view and existing error, no-op and zoom assertions. Validation: The minimized regression fails five times on PR herdrdev#4159's unchanged head and passes five times with the one-line fix. All 19 pane-move tests, native lint, maintenance, architecture and integration-asset checks pass. Full native just ci on the publication branch, including the unchanged prerequisites from herdrdev#4159 and herdrdev#4168, passes 3585 Rust tests with six default skips plus lint, 106 maintenance, six architecture and 39 integration-asset tests. All seven docs contract tests pass. Local Windows cross-lint was not run because its SDK is unavailable. No geometry policy, protocol or periodic rendering behavior is changed. * fix: preserve live selections during pane output (herdrdev#4193) refs herdrdev#3841 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> * fix(windows): preserve sgr mouse encoding after native capture (herdrdev#4195) refs herdrdev#4155 * fix(windows): normalize pane launch cwd casing (herdrdev#4199) * fix(windows): reject dynamic msvc runtime in packaging (herdrdev#4200) The v0.8.2 Windows archive shipped an herdr.exe that imported VCRUNTIME140.dll and the api-ms-win-crt-* Universal CRT, none of which the archive provides. Static CRT linkage was restored for x86_64-pc-windows-msvc in .cargo/config.toml, but nothing verified the packaged executable, so the dependency could silently regress. Parse the PE import and delay-import tables and reject herdr.exe when it depends on the dynamic Microsoft C/C++ runtime, including release and debug variants. Check the input while staging and re-check the staged executable before archiving so a direct archive cannot bypass the guard. refs herdrdev#3129 * fix: preserve unloaded sessions before replacement (herdrdev#4190) refs herdrdev#4125 * fix: preserve macos services across logout (herdrdev#4202) refs herdrdev#4100 * chore: approve vinayshah1998 as contributor * fix: keep remote surface updates incremental (herdrdev#4209) refs herdrdev#3769 refs herdrdev#3745 * fix: keep local usable when remote machines stall (herdrdev#4234) refs herdrdev#3903 * fix: let local agent clicks cancel remote handoffs (herdrdev#4236) refs herdrdev#3903 * fix: require exact session names before deletion (herdrdev#4233) * fix: require exact session names before deletion refs herdrdev#3819 * test: isolate session deletion regression tests refs herdrdev#3819 --------- Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * test: wait for app readiness before late session injection (herdrdev#4224) Root cause: The API socket is bound before App evaluates session restore. Waiting only for that socket lets the late-session test inject its file during startup, so the server may load it normally instead of exercising late-file recovery. The missing backup then fails an otherwise correct persistence assertion. Fix: Wait for a read-only workspace-list request to pass through App and assert that the workspace list is still empty before injecting the session file. Keep the existing autosave deadline and byte-for-byte backup assertion. Add no sleeps, retries, longer timeouts or production changes. Validation: The original assertion fails with unchanged master. With the barrier, five independent runs pass. Full native just ci passes 3624 Rust tests with six default skips, formatting, all-target Clippy, 112 maintenance tests, six architecture tests and 39 integration-asset tests. All seven docs contract tests pass. Local Windows cross-lint was not run because its SDK is absent. Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> * fix: offer forced removal for worktrees with submodules (herdrdev#4238) refs herdrdev#1797 * ci: promote stable releases from protected previews (herdrdev#4241) * fix: address release promotion review findings * fix: avoid workstation sdk requirement in preview checks * docs: prepare 0.9.1 release documentation * release: v0.9.1 * ci: fetch upstream release tags for documentation checks * test: remove obsolete hidden-client help assertion --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> Co-authored-by: kangal-bot <285672167+kangal-bot@users.noreply.github.com> Co-authored-by: akbash <akbash@herdr.dev> Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com> Co-authored-by: Mark Jaquith <mark.jaquith@gusto.com> Co-authored-by: Rhett CfZhuang <dark.momo985@gmail.com> Co-authored-by: Kataoka Katsuki <49934462+kataokatsuki@users.noreply.github.com> Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com> Co-authored-by: Cameron <cameron@pfiffer.org> Co-authored-by: caner-akca <94343893+caner-akca@users.noreply.github.com> Co-authored-by: Kakumanu Ashok Reddy <kakumanuashokreddyias2024@gmail.com> Co-authored-by: dengos <dengoswei@gmail.com> Co-authored-by: Eric Yue <jiacheng.yue@foxmail.com> Co-authored-by: Mark Jaquith <mark@jaquith.me>
* fix(windows): avoid lossy native Win32 input mode (#4133)
refs #3932
refs #3702
refs #3910
* fix: keep modified enter compatible with legacy panes (#4110)
* fix: keep modified enter compatible with legacy panes
refs #4103
* fix: synchronize modified key negotiation state
refs #4103
* test: cover windows legacy modified enter
refs #4103
---------
Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>
* fix: avoid resending terminal cells for metadata updates (#4137)
* feat: add cursor-based editing to text inputs (#3698)
* feat: add cursor-based editing to text inputs
refs #1803
* fix: preserve dialog actions with associated text
refs #1803
* refactor: simplify text editor outcomes and tests
refs #1803
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: protect integration configs from failed writes (#3973)
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
* fix: atomically write integration configs
refs #3970
---------
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: map pixel mouse by integer cell pitch (#4140)
refs #3295
* fix: reject stale pane surfaces after reconnect (#4141)
* test: deflake recent CI failures (#4139)
* fix(windows): batch input trace diagnostics (#4146)
refs #3932
* fix(windows): prefer VT input for SSH clients (#4148)
refs #3932
* fix: reveal the full last tab in overflowing tab strips (#4152)
refs #4151
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
* fix: reveal newly focused workspaces in the machines sidebar
* fix: propagate focused pane moves to attached clients (#4159)
refs #4153
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* test: wait for runtime readiness in integration tests (#4168)
Root cause:
The handoff test starts an agent immediately after workspace creation,
before the new shell is necessarily ready. The reconnect test assumes a
fixed sidebar row and searches raw ANSI output for text that can arrive
as separate incremental cell updates.
Fix:
Wait for a shell builtin to write a readiness marker before sending the
single raw agent.start request. Exercise the gap with a delayed shell.
Locate the recovered workspace in the rendered sidebar and check the
reconstructed screen for selection and input, including overlay borders.
Do not extend existing timeouts or add retries that hide failures.
Validation:
All 25 affected group and helper tests pass. Ten repeated rounds of the
two runtime tests and the row helper pass, for 30 test executions without
retries. Standalone native just ci passes 3582 Rust tests with six default
skips, formatting, all-target Clippy, 106 maintenance tests, six architecture
tests and 39 integration-asset tests. Windows cross-lint was not run because
its SDK is not installed.
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: reconcile client locations after no-focus pane moves (#4171)
refs #4153
Root cause:
A no-focus pane move can remove the source tab without changing the
server's numeric workspace and tab coordinates. PaneMove is not classified
as a topology change, so a client viewing that tab retains its deleted ID.
Its target lookup falls back to the server default and can follow a later,
unrelated focus change instead of staying in the source workspace.
Fix:
Include PaneMove in the existing client-location reconciliation path.
Extend the existing focus-guard test with a client on the removed tab and
verify that a later server-default change cannot move its view. Preserve
the other client's view and existing error, no-op and zoom assertions.
Validation:
The minimized regression fails five times on PR #4159's unchanged head and
passes five times with the one-line fix. All 19 pane-move tests, native
lint, maintenance, architecture and integration-asset checks pass.
Full native just ci on the publication branch, including the unchanged
prerequisites from #4159 and #4168, passes 3585 Rust tests with six default
skips plus lint, 106 maintenance, six architecture and 39 integration-asset
tests. All seven docs contract tests pass. Local Windows cross-lint was
not run because its SDK is unavailable.
No geometry policy, protocol or periodic rendering behavior is changed.
* fix: preserve live selections during pane output (#4193)
refs #3841
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
* fix(windows): preserve sgr mouse encoding after native capture (#4195)
refs #4155
* fix(windows): normalize pane launch cwd casing (#4199)
* fix(windows): reject dynamic msvc runtime in packaging (#4200)
The v0.8.2 Windows archive shipped an herdr.exe that imported
VCRUNTIME140.dll and the api-ms-win-crt-* Universal CRT, none of which the
archive provides. Static CRT linkage was restored for x86_64-pc-windows-msvc
in .cargo/config.toml, but nothing verified the packaged executable, so the
dependency could silently regress.
Parse the PE import and delay-import tables and reject herdr.exe when it
depends on the dynamic Microsoft C/C++ runtime, including release and debug
variants. Check the input while staging and re-check the staged executable
before archiving so a direct archive cannot bypass the guard.
refs #3129
* fix: preserve unloaded sessions before replacement (#4190)
refs #4125
* fix: preserve macos services across logout (#4202)
refs #4100
* chore: approve vinayshah1998 as contributor
* fix: keep remote surface updates incremental (#4209)
refs #3769
refs #3745
* fix: keep local usable when remote machines stall (#4234)
refs #3903
* fix: let local agent clicks cancel remote handoffs (#4236)
refs #3903
* fix: require exact session names before deletion (#4233)
* fix: require exact session names before deletion
refs #3819
* test: isolate session deletion regression tests
refs #3819
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* test: wait for app readiness before late session injection (#4224)
Root cause:
The API socket is bound before App evaluates session restore. Waiting only
for that socket lets the late-session test inject its file during startup,
so the server may load it normally instead of exercising late-file recovery.
The missing backup then fails an otherwise correct persistence assertion.
Fix:
Wait for a read-only workspace-list request to pass through App and assert
that the workspace list is still empty before injecting the session file.
Keep the existing autosave deadline and byte-for-byte backup assertion.
Add no sleeps, retries, longer timeouts or production changes.
Validation:
The original assertion fails with unchanged master. With the barrier, five
independent runs pass. Full native just ci passes 3624 Rust tests with six
default skips, formatting, all-target Clippy, 112 maintenance tests, six
architecture tests and 39 integration-asset tests. All seven docs contract
tests pass. Local Windows cross-lint was not run because its SDK is absent.
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: offer forced removal for worktrees with submodules (#4238)
refs #1797
* ci: promote stable releases from protected previews (#4241)
* fix: address release promotion review findings
* fix: avoid workstation sdk requirement in preview checks
* docs: publish preview documentation
* release: synchronize metadata for v0.9.1
* fix: respect opencode tui.json registrations (#4250)
refs #4240
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: cache saved machine connection metadata (#4256)
refs #4252
* fix(windows): preserve VT input during mouse refresh (#4257)
refs #4251
* fix(windows): restore terminal after setup errors (#4261)
refs #4251
* fix(windows): recover VT input without changing readers (#4269)
refs #4251
* test: make windows config backup legacy fixture explicit (#4290)
backup_preserves_legacy relied on a freshly created temp file already having a legacy DACL (D:(...)). Directories that grant inheritable ACEs produce D:AI(...) instead, so the fixture failed before exercising the backup path. Install the legacy DACL explicitly, preserving the existing ACEs while clearing the auto-inherit and protected control bits.
* fix(windows): temporarily revert VT only mode on Windows (#4295)
refs #4251
* fix(windows): prefer pwsh for the default pane shell (#4297)
* fix(windows): prefer pwsh for the default pane shell
Windows panes launched Windows PowerShell 5.1 (powershell.exe) whenever [terminal] default_shell was unset, ignoring PowerShell 7 even when pwsh.exe resolved on PATH. Resolve the unset default against PATH and prefer pwsh.exe, falling back to the inbox powershell.exe. An explicit default_shell still wins.
* fix(windows): validate pwsh before using it as the default shell
portable-pty resolves the configured shell with Path::exists and passes that path to CreateProcessW, which does not fall through to later PATH entries. Selecting an invalid pwsh.exe would therefore break new panes instead of falling back. Only prefer a pwsh.exe that starts with the PE MZ magic, and return its path so the validated binary is the one launched.
* fix(windows): validate the full PE header before preferring pwsh
Checking only the DOS MZ signature still accepted truncated images, DLLs, and foreign-architecture binaries. Because portable-pty passes the resolved path straight to CreateProcessW without trying later PATH entries, validate the PE signature, the COFF header (machine, executable-image bit, not a DLL, section count), and the optional header before preferring pwsh.exe.
* fix(windows): accept native ARM64 pwsh and validate the section table
Herdr ships an x64 Windows build that also runs on Windows ARM64 under x64 emulation, so cfg!(target_arch) cannot tell whether a native ARM64 pwsh.exe is launchable. Accept every executable machine type Windows supports instead of rejecting ARM64 on the x86_64 build. Also bound-check the section table (40 bytes per declared section) after the optional header so a truncated table falls back instead of being selected.
* fix(windows): detect the native host machine for pwsh compatibility
Machine compatibility was derived from cfg!(target_arch), which reports the emulated x64 process on Windows ARM64, and then briefly accepted ARM64 on every host. Read the native machine with IsWow64Process2 in the platform layer instead: ARM64 Windows accepts ARM64/x64/x86 images, x64 accepts x64/x86, and x86 accepts only x86.
* test: skip windows symlink tests without symlink privilege (#4311)
Windows refuses file symlinks with ERROR_PRIVILEGE_NOT_HELD (1314) unless
the test process is elevated or Developer Mode is enabled, which broke
non-admin local runs. Share a helper that reports the missing privilege so
the two symlink tests skip only the symlink-dependent assertions.
* test: add local windows input gauntlet (#4298)
* test: add local windows input gauntlet
refs #4251
* test: add local windows input gauntlet
Harden elevation and channel identity checks, distinguish known host limitations, and surface incomplete cleanup.
refs #4251
* test(windows): harden input gauntlet execution
refs #4251
* test(windows): make input gauntlet self-contained
refs #4251
* test(windows): bound input gauntlet failure paths
refs #4251
* test(windows): extend input qualification gauntlet
refs #4251
* test(windows): complete input qualification matrix
refs #4251
---------
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>
* fix(windows): preserve mouse capture during refresh (#4319)
* fix(windows): preserve mouse capture during refresh
refs #4284
* test(windows): fix default input gauntlet invocation
* fix(windows): preserve pixel mouse transitions
refs #4284
* fix: correct grok activity detection with custom or disabled osc signals (#4337)
refs #4333
* test: focus agent detection coverage on engine contracts (#4338)
* test(windows): qualify remote clipboard image paste (#4324)
* test(windows): qualify remote clipboard image paste
refs #4314
* test(windows): verify staged clipboard image contents
refs #4314
* test(windows): safely clean failed clipboard leases
refs #4314
* perf: enable ssh compression for remote connections (#4340)
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: preserve request ids in socket error responses (#4353)
* fix: preserve request ids in socket error responses
refs #4344
* docs: clarify socket error response limits
refs #4344
---------
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
* fix: reveal selected agent when cycling the sidebar (#4355)
* fix: reveal selected agent when cycling the sidebar
* fix: reveal agent after endpoint activation
* test: isolate integration assets from inherited omp environment (#4351)
refs #4346
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>
* feat: add configurable pane screen and scrollback clearing (#4383)
* feat: show every agent and terminal in the go to picker (#4384)
* fix: stabilize windows cursor redraws and settle deadlines (#4389)
* fix: prevent cursor jumps during synchronized redraws
refs #4303
* fix: settle cursor positions across delayed windows redraws
refs #4303
* fix(windows): hold jump-shaped cursor moves through the max window
* fix: bound windows cursor settling and repaint at its deadline
refs #4303
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
Co-authored-by: JJLiebig <jj@liebig.gg>
Co-authored-by: Axel Karlsson <axelalbertkarlsson@outlook.com>
* test(windows): distinguish clipboard paste origin in input gauntlet (#4394)
* test(windows): distinguish clipboard paste origin in input gauntlet
refs #4314
* test(windows): correct gauntlet paste-origin evidence
refs #4314
* fix: recover the cursor during animated redraws (#4404)
refs #4303
* fix: detect kiro status from live controls and osc signals (#4372)
* fix: detect kiro status from live controls and osc signals
refs #4203
* fix: handle variable-height kiro approval panels
refs #4203
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: detect codex status with custom interrupt keys (#4196)
* fix: detect codex status with custom interrupt keys
* test: remove unverified codex stale-status cases
* fix(detect): match punctuation in codex interrupt hints
Accept parentheses in remapped interrupt binding labels and simplify the cheap prefilter to a single any-gate shared by both manifest copies.
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: preserve delayed mouse reports with confirmed keyboard input (#4247)
* fix: preserve delayed mouse reports with confirmed keyboard input
refs #3480
* fix: capture geometry before replaying buffered mouse input
refs #3480
---------
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: keep workspace navigation visible in terminal theme (#4408)
* fix: keep workspace navigation visible in terminal theme
refs #4300
* fix: clear workspace highlight on directional pane focus
refs #4300
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: preserve explicit worktree workspace membership (#4301)
refs #4293
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>
* fix: drain event subscriptions and report history loss (#4225)
* fix: drain event subscriptions and report history loss
refs #4178
Root cause:
Each subscription emits at most one matching event per 100 ms poll, while
the shared event history retains only 512 entries. Bursts therefore build
a backlog, and an evicted cursor silently resumes at the oldest retained
event with no indication that earlier events were lost.
Fix:
Drain the bounded retained batch for lifecycle and agent-status streams.
Check for history loss while holding the same lock used to read events,
then return an events_lost error and close only the affected subscription.
Keep event envelopes, request shapes, endpoint codecs and wait behavior
unchanged. Preserve state filters, initial snapshots, event order and the
existing cadence for snapshot-only subscriptions.
Document resubscription and snapshot recovery in the unreleased API docs.
Add regressions for burst draining, startup-window loss, retention bounds,
status ordering and filtering, and unavailable history.
Validation:
Three socket regressions fail before the fix and pass afterward. Thirty-two
focused API tests and five repeated rounds of socket tests pass. The real
current-master comparison receives 516 of 600 events before the fix and
600 of 600 after it in about 0.97 seconds. A paused reader receives the
explicit error, and a new subscription plus snapshot recovers successfully.
Full native just ci on the dependency-inclusive branch passes 3631 Rust
tests with six default skips, formatting, all-target Clippy, 112 maintenance
tests, six architecture tests and 39 integration-asset tests. All seven
docs contract tests pass. No retries or additional exclusions were used.
Local Windows cross-lint was not run because its SDK is unavailable.
* test: cover subscription isolation across platforms
refs #4178
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: reject terminal-less attach before starting a session (#4395)
refs #4393
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: confirm closing the last tab in the tui (#4409)
* fix: confirm closing the last tab in the tui
refs #4379
* docs: clarify last-tab worktree confirmation in translations
refs #4379
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: preserve session layouts across shutdown and restore failures (#4400)
* fix: preserve session layouts across shutdown and restore failures
refs #4320
* test: update subscription fixture for pane restore errors
---------
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* docs: publish preview documentation
* perf: index navigator tabs and panes per endpoint (#4426)
* chore: approve ain3sh contributor accounts
* fix: refresh forwarded ssh agents after reconnect (#4443)
refs #1931
* fix: honor droid scrollback clear requests in panes (#4432)
Remove the droid-specific filter that discarded ED3 before PTY output
reached the terminal parser. Keeping stale scrollback beneath replayed
primary-screen frames duplicated the welcome header and old transcript.
Pass the original bytes to the parser and trackers. Add process-aware
regression coverage across PTY chunk boundaries and preserve ED2/ED3
screen-history separation and alternate-screen isolation.
Co-authored-by: ain3sh <ainesh.chatterjee@gmail.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
* fix: distinguish agent completion from startup and session changes (#4457)
refs #3338
refs #3990
refs #3916
* fix: clear inherited terminal and agent session identity (#4461)
refs #4104
* fix: stagger restored agent startups (#4487)
* fix: stagger restored agent startups
refs #4102
* fix: report startup delay changes requiring restart
refs #4102
* fix: track opencode subagent status per pane (#4357)
refs #1362
* fix: preserve hook agent status across live handoff (#4493)
* fix: recognize codex mention completion popups (#4495)
refs #4220
* fix: reuse ssh authentication and add machine recovery commands (#4490)
* fix: reuse ssh authentication and add in-app mfa recovery
refs #3763
* refactor: move ssh recovery to machine cli
refs #3763
* fix: move worktree lookups async (#4492)
* fix: recognize Windows daemons in non-killing jobs (#4489)
refs #4309
* fix: detect npm-installed omp on windows (#4502)
refs #4313
* fix: keep synchronized pane frames atomic (#4508)
refs #2968
* fix: prevent Windows non-text keys from triggering the space prefix (#4510)
refs #4470
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
* test(windows): capture native console input records (#4520)
refs #4470
* test: wait for complete handoff pid markers (#4538)
* test: wait for complete handoff pid markers
* test: wait for complete handoff pid markers
* fix: prevent cursor flicker on status redraw (#4554)
* fix: prevent cursor flicker on status redraw
* fix: avoid stale cursor during resize
* fix: avoid inferring codex idle from terminal output (#4563)
refs #4507
* fix: match navigator search terms independently (#4535)
* fix: match navigator search terms independently
refs #4273
* docs: remove redundant navigator search note
refs #4273
* fix(client): restore combined begin() convenience after prepare/start split
The merge silently took upstream's two-phase prepare()+start() split for
PendingEndpointActivation under the fork's begin() name, losing the
single-call semantics 17+ existing fork tests were written against (caught
by running the test suite, not by cargo check -- Rust's auto-reborrow from
&mut to & let every call site still compile against the wrong body).
Renames the split entry point back to prepare() (matches its real upstream
semantics and the one production caller in shell_runtime.rs, which needs
the split to interleave abandoning a stale pending activation between
prepare and start). Restores begin() as a #[cfg(test)]-only prepare()+start()
convenience wrapper so none of the existing test call sites need editing.
refs upstream sync b7781a67
* fix(tests): rename remaining upstream binary-name references to bora
tests/auto_detect.rs, tests/host_shutdown.rs, and tests/session_delete.rs
(new upstream test files merged by this sync) still hardcoded the
herdr-dev debug namespace string; caught by
tests/upstream_wiring.rs::no_source_file_references_the_upstream_binary_name.
Also adds the four new upstream-only platform-gated files
(src/platform/linux/{config_file_tests,shutdown}.rs,
src/platform/windows/config_backup.rs and its tests.rs) to
PLATFORM_GATED_ALLOWLIST on both macOS and Linux, caught by
no_source_file_is_orphaned_from_the_build.
refs upstream sync b7781a67
* fix(detect): restore fork's osc_title_idle rule for codex
The merge silently replaced src/detect/manifests/codex.toml with upstream's
version wholesale (identical byte-for-byte to upstream/master, no conflict
marker, because fork's own edit history to this file happened before the
prior sync's base and so carried zero local delta git could detect) --
dropping the fork's own low-priority osc_title_idle fallback rule.
Combined with upstream's unrelated 'fix: avoid inferring codex idle from
terminal output (#4563)' (which changed the state fallback for an
agent==Codex screen with no matched rule from Idle to Unknown), the missing
rule surfaced as 9 real test failures: screens that used to fall through to
an assumed-idle default now correctly report ambiguous state, except the
osc_title_idle rule that should have matched first and reported real idle
evidence was gone.
Restored verbatim from the pre-merge fork manifest (git show
ad415672:src/detect/manifests/codex.toml). All 9 previously-failing codex
detection tests pass again.
refs upstream sync b7781a67
* style: cargo fmt after post-merge fixes
refs upstream sync b7781a67
* fix: add clippy allow justification for test-only begin() convenience
refs upstream sync b7781a67
* fix: clean clippy lints introduced by the merge
Redundant closures (persist/snapshot.rs, server/headless/{lifecycle,render,
retained_surface}.rs, ui/panes.rs), cast-lossless u32/u16->u64 conversions
in the Windows PE-header sniffing helper (pane.rs), and a redundant
to_string()+implicit-clone on an already-owned String error in the new
pane.clear handler (app/api/panes.rs, part of the merged clear-screen
feature). 'just lint' is green.
refs upstream sync b7781a67
* fix(windows): add missing AgentPromptParams fields in windows-only test
Caught only by the Windows cross-target cargo check (this test body is
#[cfg(windows)]-gated so macOS/Linux never compile it); the struct gained
from_pane/when_idle/when_idle_timeout_ms/peer_pid/origin_channel from the
merge and this one windows-only fixture wasn't updated.
refs upstream sync b7781a67
* docs: add binding rule on silent semantic drift in non-conflicting merge hunks
Three instances this sync (activation.rs begin/prepare split, worktrees.rs
async-discovery rename, codex.toml whole-file silent replacement) where a
correctly-resolved conflict marker sat next to a DIFFERENT non-conflicting
hunk that changed real behavior without tripping cargo check, caught only
by running the affected test module.
refs upstream sync b7781a67
* fix(protocol): decode force_full_repaint in the surface-delta codec
decode_frame() hardcoded force_full_repaint: false instead of reading it
off the wire, but the serde-based encoder (message()) writes the real
bool value unconditionally (FrameData has no skip_serializing_if on it,
per the bincode-is-positional rule in AGENTS.md). Every encoded surface
delta therefore carried one extra trailing byte per frame (main frame and,
when present, popup frame) that the hand-written bincode::Decode reader
never consumed, desyncing every field read afterward on that connection -
observed as unrelated enum/collection decode errors ('found: 5' on
SurfaceGraphicsSource, 'popup cell update has no popup metadata',
'InvalidIntegerType'). Fixed by decoding the trailing bool in decode_frame
at the same position FrameData actually serializes it (after graphics).
Also updated the surface_delta_v1_binary_fixture_is_frozen digest: it is a
genuinely correct shape change (this fork keeps force_full_repaint, already
covered by the PROTOCOL_VERSION 22->23 bump), not a bug - same class as the
two already-updated frozen hashes in wire.rs.
refs upstream sync b7781a67
* test: port upstream's dropped OpenCode-disabled and Windows socket-marker-path coverage
The fork's shared herdr-agent-state.test.ts rewrite moved to a single
shared-import-at-module-load harness and, in doing so, dropped upstream's
entire generic `integrations`/`socketPlugins` parametrized coverage for:
- the "stays disabled without HERDR_SOCKET_PATH" guard (every integration)
- the win32 marker-path -> named-pipe endpoint mapping (every integration)
Ported the OpenCode pair into opencode/herdr-agent-state.test.ts next to its
existing per-test-fresh-import harness (captures the real connection path
instead of ignoring it). Ported Pi/Oh My Pi/Kilo's marker-path case into a
new windows-socket-marker-path.test.ts using mock.module (a raw net.
createConnection mutation, matching upstream's exact original technique,
corrupted an unrelated omp test in a different file when bun ran multiple
test files in one process - confirmed this repo always runs each test file
through its own `bun test <file>` invocation via the justfile, never
combined, so isolated per-file behavior is what actually matters).
Writing this test surfaced a real, pre-existing (predates this sync) bug:
pi/herdr-agent-state.ts never mapped HERDR_SOCKET_PATH through the win32
named-pipe convention every other integration (omp, kilo, opencode) uses -
it called createConnection(socketPath) directly, so Pi users on Windows
would never actually connect. Fixed by adding the same socketEndpoint
mapping upstream's own copy of this file already has, and aligning the
import to the same `net.createConnection` style as the other three
integrations (the file's prior `import { createConnection }` named import
is what made this un-mockable and is why no test ever caught it).
refs upstream sync b7781a67
* fix(windows): validate the staged bora.exe, not a stale herdr.exe literal
validate_stage() re-checks the staged executable's MSVC runtime linkage
after staging (it isn't hash-pinned since it changes every build), but
hardcoded the literal "herdr.exe" while stage_bundle() next to it already
stages the binary as "bora.exe" (this fork's rename) - so validate_stage
silently checked a file that was never written and only worked by accident
whenever nothing else in the stage directory happened to be named
herdr.exe. Caught by just check's maintenance-test suite once run to
completion (FileNotFoundError, not a false pass, since no herdr.exe exists
in a bora stage). Updated the swap-and-recheck test fixture to match.
refs upstream sync b7781a67
* fix(protocol)!: delete dead FrameData.force_full_repaint, restore upstream digests
Reviewer finding (M5, highest priority): 8d2fb3c3 re-blessed the frozen
generation-1 surface_delta digest to a fork-shaped value instead of fixing
the actual bug. That codec name ("surface_delta") is shared wire-protocol
surface with upstream herdr servers - a bora client attached to an upstream
remote would decode deltas one byte off, exactly the class of bug 8d2fb3c3
was fixing for THIS fork's own client, just relocated to the interop
boundary instead of eliminated. AGENTS.md "Stable client endpoint contract"
forbids re-blessing a frozen digest instead of matching it.
Root cause: FrameData.force_full_repaint has no producer and no reader
anywhere in this codebase (git grep confirms only decode.rs ever touched
it, always hardcoded to false) - it is dead weight from an earlier,
apparently abandoned repaint-forcing feature. request_full_repaint (the
caller-side half of that feature) was already removed in 27c65c27.
Deleted the field and its ~44 initializers
across 8 files. Restored both wire.rs frozen-digest tests
(client_shell_graphics_payload_codec_is_frozen, and the PaneSurface
roundtrip above it) and surface_delta.rs's frozen fixture to their exact
upstream values (verified by recomputing after the field's removal, not
by trusting upstream's line-adjacent digest, since one of upstream's own
literals had independently drifted). Removed the now-stale "22 -> 23"
comments citing ceo-bora#274 (that PROTOCOL_VERSION bump's stated reason -
retaining this field - no longer applies; kept the version at 26, see the
next commit, for the actually-incompatible removal itself).
refs upstream sync b7781a67
* Revert "fix(detect): restore fork's osc_title_idle rule for codex"
This reverts commit 9fa1f9b36756c381dea3f5c7cb4494dd680b6e05.
* fix(protocol): update hardcoded PROTOCOL_VERSION=25 fixtures to 26
Fallout from the previous commit's version bump: tests/support/mod.rs kept
its own hand-copied CURRENT_PROTOCOL constant instead of referencing
crate::protocol::PROTOCOL_VERSION, and tests/api_ping.rs asserted the
literal value directly. Regenerated docs/next/api/herdr-api.schema.json via
HERDR_UPDATE_API_SCHEMA=1.
refs upstream sync b7781a67
* fix(detect): reconcile manifest test file with upstream's consolidation
Reviewer finding (B1). Two compounding mistakes:
1. The merge resolved src/detect/manifest/tests.rs conflicts by keeping the
fork side wholesale ('ours'), silently resurrecting roughly 37 tests
upstream had deliberately deleted in its own test-suite consolidation
(7201907b 'focus agent detection coverage on engine contracts', plus
9c96f7dd's own 20-line trim) — none of those deletions were fork-specific
drift to preserve.
2. A separate earlier commit on this branch (now reverted) then restored
src/detect/manifests/codex.toml's osc_title_idle rule believing it was a
fork feature the merge dropped. git log -S proves the rule was upstream
AUTHORED (Ogulcan Celik, 82468a0e) and upstream DELETED (9c96f7dd #4563,
fixing herdrdev/herdr#4507 - Codex reported idle while actively working).
The fork never touched this file before this sync (git diff ad415672
42b6afbd is empty), so it carried zero local delta to lose.
Fix: replaced this file with upstream's current (post-consolidation) tests.rs
verbatim, then re-appended only the tests git log -S confirms are genuinely
fork-authored (9 tests: omp_manifest_reads_state_from_title_not_body,
osc_title_state_reads_only_title_rules,
osc_title_state_is_none_for_manifests_without_a_title_idle_rule, and the
6 omp_osc_title_*/omp_selection_prompt_outranks_working_title/
omp_state_less_title_has_no_matched_rule cluster), plus a local osc_explain
test helper (upstream's own copy of it, a thin explain_with_input wrapper,
was deleted alongside the OSC test section it served; every other helper
these tests need - osc_title_state, osc_title_state_for_loaded,
with_manifest_dirs, explain, parse_manifest, loaded_manifest - is still
live, unmoved code). Adjusted one API drift: the fork's
test_config_env_lock() returns a parking_lot::Mutex (no .unwrap() on
.lock()), not std::sync::Mutex.
python3 scripts/agent_detection_manifest_check.py --require-all-published
now exits 0.
refs upstream sync b7781a67
* fix(ci): restore preview.py subcommands and fix workflow_dispatch guard
Reviewer finding (B2). Three non-conflicting upstream hunks silently broke
the fork's workflow_dispatch preview model, which this sync's merge
otherwise deliberately kept (preview.yml stayed on workflow_dispatch, not
upstream's push:tags model):
1. preview.yml's Select preview commit step kept a guard
(if [ "$GITHUB_REF_NAME" != "$tag" ]) that only makes sense when the
workflow is triggered by a tag push. On workflow_dispatch,
GITHUB_REF_NAME is the branch name (main), so the guard failed on every
single run. Removed it.
2. scripts/preview.py's select-commit subcommand (and the
latest_publishable_commit/hidden_subject helpers it needs, which skip
auto-generated docs:/chore: bot commits when picking a preview
candidate) was deleted by upstream's own simplification. preview.yml
still calls it. Restored the subcommand and its helpers from the
pre-merge fork state (git show ad415672:scripts/preview.py); the fork
never touched this file itself so its pre-sync shape came entirely from
upstream's own pre-simplification code, not fork authorship.
3. Upstream also simplified build_notes() to a bare "view changes" link,
dropping the --base-version argument and the conventional-commit
grouping (Added/Fixed/Performance/Maintenance) the fork's preview
release notes use. preview.yml still passes --base-version. Restored
the richer build_notes signature, its helper functions
(commit_subjects/humanize_subject/TYPE_HEADINGS/COMMIT_RE), and the
notes subcommand's --base-version argument.
Also restored the "Prune old preview prereleases" step upstream dropped
along with its own rejected protected-preview model (review m4) - the
fork's dispatch model still publishes unbounded preview releases without
it.
scripts/test_preview.py: replaced the one test asserting upstream's
now-reverted minimal notes format with tests covering the restored rich
format (grouped-by-heading body, base-version line, and the no-commits
fallback), and kept every other test upstream's consolidation improved
(hotfix/stable range-base tests, asset archiving). 12/12 pass.
refs upstream sync b7781a67
* fix(release): restore fork's simple release-prepare/publish recipes
Reviewer finding (B3). The merge silently replaced the fork's
release-prepare/release-publish/release justfile recipes with upstream's
preview-promotion versions (new $version $preview positional args,
scripts/release.py check-source calls, and a new preview recipe that
fetches origin/master and calls scripts/release.py preview-source) even
though the conflicted hunks looked resolved - only the recipe bodies
conflicted; the signatures and the check-source call lines merged silently
non-conflicting.
scripts/release.py implements a two-stage preview-then-promote model this
fork doesn't use (it hardcodes herdr-* asset names, defaults to
herdrdev/herdr, expects origin/master, and requires annotated release tags
with Preview:/Previous-Stable: trailers the fork's own tag command never
writes) - none of that is reachable through any justfile recipe now.
Restored the three recipes verbatim from 42b6afbd:justfile (single
$version argument, no external release.py dependency, the fork's own
ancestry/clean-tree/version-match checks) and dropped the preview recipe
entirely - the fork publishes previews via preview.yml's workflow_dispatch
trigger, not a pushed tag.
Known gap, out of scope for this fix: .github/workflows/release.yml's
validate-release-source and update-nix-package jobs both still gate on
`github.repository == 'herdrdev/herdr'` (present since before this sync,
predates it) - both are dead on this fork's actual repository regardless
of scripts/release.py's own internal incompatibilities. Flagging for a
follow-up since fixing the gate without also reworking check-tag's
ASSETS/repo/branch/tag-trailer assumptions would turn a silently-skipped
job into a job that runs and always fails.
refs upstream sync b7781a67
* fix(ci): unwire release-workflows.test.ts pending a fork-shape rewrite
Reviewer finding (B4). This upstream-added test asserts an upstream-only
publishing model (tag-push triggers, herdr-* asset names, actor rechecks
gated on github.event_name push) this fork does not use, so it fails 4/5
on every run through maintenance-test (reachable from every just check and
just ci CI leg). Removed it from the maintenance-test recipe with a
comment explaining why; kept the file for reduced conflict surface on the
next upstream sync rather than deleting it.
refs upstream sync b7781a67
* fix(docs): restore docs/preview wholesale from origin/main
Reviewer finding (B6). AGENTS.md: docs/preview is Preview-CI-owned generated
output and must never be hand-edited or three-way merged - the exact source
mismatch the merge produced (40 files hybridized between the fork and
upstream's snapshot at merge time) is what node scripts/docs/preview.mjs
check now flags as an integrity failure, and Preview itself cannot repair
it while B2 (workflow_dispatch guard) stood. Restored the entire directory
from origin/main (42b6afbd) wholesale, matching the rule; the next real
Preview run is the only legitimate way this content changes.
node scripts/docs/preview.mjs check now reports the same snapshot digest
as origin/main.
refs upstream sync b7781a67
* fix(deps): minimally resolve Cargo.lock instead of full regeneration
Reviewer finding (M1). 8d2fb3c3 deleted the conflicted Cargo.lock and ran
cargo generate-lockfile, which re-resolved the whole dependency graph and
silently upgraded 92 crates that neither this fork nor upstream asked for
in this sync (tokio, clap, libc, mio, serde_json, time, uuid,
wasm-bindgen, and more) - an unreviewed, unbisectable dependency bump
riding inside a merge commit.
Restored origin/main's Cargo.lock byte-for-byte as the starting point and
let cargo resolve only the three new upstream dependencies
(unicode-segmentation, zbus, futures-util) plus their transitive closure.
Verified by diffing package/version pairs between the two locks: bora's
own version is the only pre-existing package whose primary pin changed
(0.49.2 -> 0.49.3, matching Cargo.toml); every other diff is either a
brand-new package (zbus and its 24 transitive dependencies) or a second,
additional version of a crate already pinned (syn, toml_edit,
toml_datetime, winnow each keep their old pinned version AND gain a
separate newer entry, because zbus's proc-macro chain needs a newer major
version than the rest of the tree already used - cargo lockfiles support
this natively without touching the existing pin). Zero unrelated crates
moved.
refs upstream sync b7781a67
* docs: restore fork's release-channel model and fix stale repaint mechanism
Reviewer findings (M2, m9). Two unrelated AGENTS.md sections drifted from
upstream's non-conflicting hunks:
1. Release Channels (~line 796) described upstream's tag/admin/preview-
promotion flow (previews select a commit from master and are tag-
triggered on preview-* pushes, just preview <ref>/just release
<version> <preview-tag>, release-tags ruleset, curated categorized
preview notes) - none of which this sync's merge actually kept in the
workflows (B2/B3 restored the fork's workflow_dispatch preview and
single-argument release recipes). Replaced with the fork's own
dispatch-model paragraphs (git show 42b6afbd:AGENTS.md), correcting
two pieces of pre-existing staleness while already touching this text:
default branch is main not master, and the published asset/binary
names are bora-* not herdr-* (both verified against the actual current
workflow files and package script this session).
2. The "layout changes must force a repaint" binding rule (~line 67)
still named AppState::request_full_repaint()/FrameData.force_full_repaint
as the routing mechanism; that field was deleted from the wire protocol
this session (see the force_full_repaint removal commit) and its
caller-side half was already gone before this sync (27c65c27) - the
whole described mechanism has had zero producers or readers for a
while. Rewrote the rule to point at the actual current mechanism
(outcome.repaint on ClientShellInput, consumed by compose() in the
client-owned 0.9.0 shell architecture) while keeping the original
incident history as context for why the constraint exists.
refs upstream sync b7781a67
* fix(client): confirm client-side before closing the last tab of a group
Reviewer finding (M3). open_close_confirmation had an early return -
"if tab_id.is_some() && closes_group { return false; }" - deferring to
what its own comment called "the existing server confirmation path": the
server used to answer a TabClose that would cascade-close a worktree
group with a confirmation_required error, which the client would then
turn into a confirm dialog. That server behavior was removed in
2d839ce6 (close_selected_workspace only ever closes the one selected
workspace now, unconditionally, no confirmation round trip). With no
server-side confirmation left and this client-side path disabled for
exactly the case that needed it, closing the last tab of a non-linked
group-root workspace in Repo view skipped confirmation entirely even with
confirm_close: true.
Removed the early return so this case reaches the same dialog-building
code every other close path already uses. accept_close_confirmation's
tab_target branch sends a plain TabClose (not a cascading WorkspaceClose),
matching upstream's own current close_tab.rs test expectations for this
exact scenario (last_tab_close_preserves_parent_group_and_linked_workspace_scope,
both linked and non-linked branches resolve to a plain tab close after
one Enter).
Updated the fork's own version of that same test: its non-linked branch
still encoded the removed server round trip (asserted an immediate
uncomfirmed TabClose, then manually injected a confirmation_required
endpoint error to drive the dialog open). Since the server can no longer
send that error, this was pinning dead behavior. Rewrote it to assert the
dialog opens immediately with title "Close worktree group?" and Enter
sends the tab close - the linked branch (which was already correct,
closes_group is false there since a linked closing workspace never joins
a group) is unchanged.
refs upstream sync b7781a67
* fix(input): let host-reply discard-arming preempt disambiguation hold
Reviewer finding (M4). flush_timeout()'s kitty-keyboard disambiguation hold
(host_escape_disambiguation_active && starts_with_bounded_incomplete_escape_sequence)
ran before the host-cell-size and color-scheme discard-arming branches
below it. starts_with_bounded_incomplete_escape_sequence accepts "\x1b["
plus any byte in 0x20..=0x3f - the same digit/`;` range a truncated
XTWINOPS cell-size reply's parameters are made of - so with disambiguation
active, a truncated host reply head (e.g. "\x1b[6;21" after its terminator
never arrived) matched the broader disambiguation check first and got held
forever instead of reaching the more specific check that would arm
discard_until and let it actually get discarded. Whatever the user typed
next got appended to that same held buffer and never reached the pane as
real keystrokes.
Fixed by excluding the disambiguation hold specifically when we are
awaiting a matching host reply (host_cell_size_replies_awaited with a
buffer matching starts_with_incomplete_host_cell_size_report, or
host_appearance_reply_awaited with starts_with_incomplete_host_color_scheme_report),
deferring to those more specific checks instead. Kept disambiguation's
hold in its original position for every other case (an earlier attempt
that moved the whole check to the end of the function broke the SGR-mouse
disambiguation-hold test, since some ambiguous SGR mouse prefixes also
overlap the host-reply-specific checks that would then run first).
Added a regression test mirroring the existing
malformed_host_reply_tail_preserves_following_input test with
disambiguation active - reproduced the bug first (typed characters after
the reply's terminator came back empty instead of ["a","b","c"]), then
confirmed the fix restores it. Full raw_input test suite: 110/110 pass,
including the disambiguation-vs-SGR-mouse test the first (reverted)
attempt broke.
refs upstream sync b7781a67
* fix(persist): route snapshot-history reads through the tolerant parser
Reviewer finding (m5). preserve_snapshot_history() parsed the pre-existing
on-disk session file with a strict serde_json::from_slice::<SessionSnapshot>,
bypassing the tolerant RawSessionSnapshot boundary (parse_snapshot in
snapshot.rs) that view_mode is specifically designed to go through - an
unrecognized value (a retired variant like the old ViewMode::Project,
ceo-bora#270, or a newer value written by a future build) fails a strict
parse for the WHOLE document and silently skips archiving an otherwise
perfectly readable session into history. Recovery history existing for
exactly this kind of migration edge case is the point of preserving it.
Routed both call sites (the current file and, when comparing layout
fingerprints to decide whether to skip a duplicate rotation, the previous
snapshot file) through parse_snapshot() instead. The explicit
version-too-new check became redundant (parse_snapshot already rejects
it) and was removed.
Added a regression test: writes the fixture session with view_mode
overwritten to the retired "project" string directly to the writer's
path, then asserts save() still produces a snapshot-history entry instead
of silently skipping it.
refs upstream sync b7781a67
* fix: rename remaining herdr binary-name leaks to bora
Reviewer findings (m1, m2). New user-facing hints/usages this sync's merge
brought in still said the upstream binary name:
- cli/machine.rs: "usage: herdr machine status/reconnect" (x2), the
reconnect terminal-required hint
- client/shell/machine_diagnostics.rs: the two client-side notice actions
that echo those same commands back to the user
- session.rs: "usage: herdr session attach" (x2, both branches) and the
"use the spelling shown by `herdr session list`" hint
m2: tests/session_delete.rs:36 and tests/auto_detect.rs:248 built their
own app_dir path with `if cfg!(debug_assertions) { "bora-dev" } else {
"herdr" }` - 8c623a40 fixed only the debug arm. The real production
app_dir_name() (src/config/io.rs) returns "bora" for the release profile,
matching the fork's own test harness convention elsewhere
(tests/client_mode.rs:416-420 already uses "bora"). Fixed the release arm
in both.
Fixed one regression this caused: a client-side test asserted the literal
old "herdr machine reconnect ..." notice text.
TERM_PROGRAM=herdr (src/pane.rs) is left unchanged per the review: it is
a deliberate ecosystem identifier decision the fork already made (see
AGENTS.md rename-leaks section), not a leak.
Scope note: a broader sweep (cli.rs, cli/workspace.rs, remote.rs,
update.rs and their tests) still has "usage: herdr ..." strings that
predate this sync and were not part of the reviewer's cited evidence;
left untouched as out of scope for this fix, flagged in the sync log.
refs upstream sync b7781a67
* style: cargo fmt after review-round fixes
* fix(tests): replace hardcoded protocol 25 literals with CURRENT_PROTOCOL
Reviewer finding R2-B1 (blocker). tests/cli/sessions.rs still asserted
protocol 25 in six places after the PROTOCOL_VERSION 25->26 bump. The
file only compiles under #![cfg(all(unix, not(target_os = "macos")))],
so this could not show up on a macOS run and would only fail on CI's
ubuntu-latest leg. Replaced all six literals with the shared
tests/support::CURRENT_PROTOCOL constant (already re-exported through
tests/cli/harness.rs and in scope via sessions.rs's existing
'use super::harness::*'), matching the convention every other
CURRENT_PROTOCOL-consuming test file already follows, so the next bump
cannot miss them.
Swept the whole tree (every linux/windows-gated file: tests/cli.rs,
tests/machine_api.rs, tests/auto_detect.rs, src/platform/{linux,windows}.rs,
plus everything under tests/cli/) for any other protocol-version 25
literal; none found (the remaining 25s in that set are all
Duration::from_millis(25) timing values, unrelated).
Verified with the linux cross cargo check --all-targets (0 errors).
refs upstream sync b7781a67
* fix(client): close the whole group when a group-root's last tab closes
Reviewer finding R2-M1 (major), a real regression introduced by aa14ae4d.
After aa14ae4d removed overlay_input.rs's early return, a last-tab close
of a non-linked worktree group root reaches open_close_confirmation and
correctly shows "Close worktree group? - N workspaces, M panes", but
accept_close_confirmation always built Method::TabClose whenever the
overlay carried a tab_target - it never looked at whether the dialog it
was confirming was actually the group variant. The dialog's title said
group, the accept sent a single-tab close: the fork server's
handle_tab_close closes only that one workspace, leaving every sibling
worktree in the group open.
aa14ae4d's commit message claimed upstream's own close_tab.rs test
expects a plain TabClose in this branch. That is false: upstream's test
(git show b7781a67:src/client/shell/tests/close_tab.rs, non-linked
branch) expects
Method::WorkspaceClose(params) if params.workspace_id == "ws_1" &&
params.close_group. The fork's own rewritten
last_tab_close_preserves_parent_group_and_linked_workspace_scope test
encoded the same wrong expectation (assert_tab_close in both branches)
and did not catch this. Not correcting via amend, per policy - this
commit is the correction.
Fix: ClientTabCloseConfirmation now carries the closes_group bool
open_close_confirmation already computes when it builds the dialog.
accept_close_confirmation reads it back: when true, it sends
WorkspaceClose{workspace_id, close_group:true} instead of TabClose,
matching what the dialog told the user and what the server's
close_selected_workspace group-close path actually does. Updated the
existing test to assert TabClose only for the linked-worktree branch
(correctly excluded from grouping) and added
assert_workspace_close_group for the non-linked group-root branch.
Verified: cargo check --all-targets clean; cargo nextest -E
'test(close_tab) or test(close_drags)' -> 9/9 pass, including the fixed
regression test.
refs upstream sync b7781a67
* fix(integration): bump pi and omp asset versions past v0.46.9 content changes
Reviewer findings R2-M3 (major) and R2-m1 (minor). Integration asset
versions are migration versions relative to the latest RELEASED tag
(v0.46.9, per distribution/latest.json and AGENTS.md), not relative to
this merge's parents. registry.rs treats installed_version >=
expected_version as Current, so an unmoved marker means every install
made from v0.46.9 silently never gets a real content change.
- pi/herdr-agent-state.ts: content changed since v0.46.9 (this sync
brought in upstream #3729's Windows named-pipe socketEndpoint mapping,
plus 060cdced's own fix to actually wire it for pi). Version marker
was still 9. Bumped to 10 (HERDR_INTEGRATION_VERSION marker and
PI_INTEGRATION_VERSION constant), once.
- omp/herdr-agent-state.ts: content changed since v0.46.9 (upstream
#3994's OMPCODE nested-session guard, landed via the PREVIOUS sync
8ac008ea - predates this merge, but the version was never bumped for
it either). Version marker was still 11. Bumped to 12
(HERDR_INTEGRATION_VERSION marker and OMP_INTEGRATION_VERSION
constant), once.
opencode is intentionally left at 13 (unchanged): it went 11->12->13
across the previous and this sync, i.e. two bumps against the
'bump once' convention, but that comes from matching upstream's own
plugin-path numbering rather than an extra fork bump - noted as an
accepted exception, not fixed further, per R2-m2.
Verified: cargo check --all-targets clean; cargo nextest -E
'test(integration)' -> 190/190 pass.
refs upstream sync b7781a67
* docs(agents): record the TERM_PROGRAM=herdr decision as binding
Reviewer finding m6 (minor, round 2). pane.rs still sets
TERM_PROGRAM=herdr; the earlier fix commit (40105cde) and the sync log
claimed this was left 'per the review's own guidance' citing an
AGENTS.md rename-leaks section, but no such AGENTS.md text existed -
the decision was never actually recorded anywhere. Added it to the
binary-rename bullet under 'Fork merge friction' as an explicit, dated,
binding line: TERM_PROGRAM stays herdr on purpose, because it's an
ecosystem/terminfo identifier other tools key off, not a user-facing
string.
refs upstream sync b7781a67
* fix(tests): update stale pi integration version literal in linux-gated sessions.rs
CI check (ubuntu-latest) failure: integration_commands_run_locally_when_server_is_missing
asserted "pi: current (v9)" after a fresh pi integration install, but
PI_INTEGRATION_VERSION was bumped 9->10 earlier in this sync
(0edb4320) for an unrelated reason (pi's asset content changed since
v0.46.9 with the version marker unmoved). This test file only
compiles on unix/non-macos
(tests/cli/sessions.rs:1 #![cfg(all(unix, not(target_os = "macos")))]),
so a local macOS cargo check/nextest run never saw it - same blind
spot class as R2-B1's protocol-25 literals in the same file, caught
this time by CI's ubuntu-latest leg instead of locally.
Swept the rest of this file and the other macOS-gated files
(tests/auto_detect.rs, tests/machine_api.rs, tests/cli.rs,
src/platform/{linux,windows}.rs) for other hardcoded
'current (v'/'outdated (v'/pi:/omp: literals; this was the only one.
The other protocol-version assertions in this same file already
derive from CURRENT_PROTOCOL (R2-B1).
Verified: cargo check --target x86_64-unknown-linux-gnu --all-targets
clean. Cannot run this test locally (macOS host); relying on CI's
check (ubuntu-latest) leg to confirm the runtime assertion.
refs upstream sync b7781a67
* fix(tests): allowlist new macos-only child files for the linux orphan-file check
CI check (ubuntu-latest) failure: no_source_file_is_orphaned_from_the_build
flagged src/platform/macos/bootstrap.rs and
src/platform/macos/config_file_tests.rs as unreachable from the Linux
build. Both are new upstream files added by this sync (commits
266b8e38, 5e38e5fb), correctly mod-declared inside macos.rs
(compiles fine on macOS and both cross-target checks), but macos.rs
itself is the platform-gated root on Linux and its own allowlist
entry does not cover its children - the same pattern already handled
for linux.rs's children (config_file_tests.rs, shutdown.rs) in the
macOS-side allowlist, just missing the symmetric entries on the
Linux side for these two newly-added files.
Swept every other new file from the upstream merge
(git diff --diff-filter=A ad415672..upstream/master -- 'src/**/*.rs')
for the same class of gap: the only other platform-gated new files
(platform/linux/{config_file_tests,shutdown}.rs,
platform/windows/config_backup{.rs,/tests.rs}) were already correctly
allowlisted on both sides. platform/ssh_agent.rs is #[cfg(unix)], so
it needs no entry in either list (both macOS and Linux are unix).
Verified: cargo check --target x86_64-unknown-linux-gnu --all-targets
clean; cargo nextest -E 'test(no_source_file_is_orphaned_from_the_build)'
passes locally on macOS (exercises the macOS-side branch of the same
test, unaffected by this Linux-side edit). Cannot exercise the
Linux-side branch locally; relying on CI's check (ubuntu-latest) leg.
refs upstream sync b7781a67
* fix(scripts): rename herdr.exe -> bora.exe in the windows input qualification tool
Cubic review finding (P2, confidence 10): 'just test-windows-input' cannot
run in this fork by default. scripts/test_windows_input.ps1 is a brand-new
upstream file (this sync), never existed on origin/main before. Its default
-ExePath resolution built and looked for release/herdr.exe, but this fork's
Cargo.toml renames the [[bin]] to bora, so cargo build --release produces
bora.exe; the staged package name from this fork's own
scripts/package_windows_conpty.py (unchanged, already correctly renamed) is
also bora.exe (confirmed: staging / "bora.exe" at package_windows_conpty.py:275).
So the default invocation path threw a file-not-found before ever launching
a qualification run.
This is exactly the 'binary rename in user-facing strings' class named in
AGENTS.md's Fork merge friction rule, so in scope for this sync unlike the
deeper upstream-authored logic bugs cubic also flagged in this same tool
(mouse-mode cleanup on early exit, exit-status validation) which are left
alone: this script only runs manually, is explicitly excluded from CI
('deliberately excluded from normal CI' per its own header comment), and I
have no Windows host to exercise or verify a deeper behavioral fix.
Left scripts/windows_input/report.py's 'herdr'/'herdr-remote' internal
matrix path-identifier literals alone: those are data-level case keys
threaded through report.py's own matrix/report generation logic, not a
display string, and renaming them would require touching upstream's test
taxonomy well beyond a filename swap with no way to verify the result.
Verified: no pwsh available locally to syntax-check; this is a narrow
string-literal change, cross-checked against package_windows_conpty.py's
actual output filename. python3 -m unittest scripts.test_windows_cross
still passes (unrelated but adjacent windows-tooling test, 27/27).
refs upstream sync b7781a67
* fix(server): refuse local attach to a server below MIN_COMPATIBLE_SERVER_PROTOCOL
Independent review finding F-D2 (C35, C88), most important item of the
follow-up round: PROTOCOL_VERSION 26 (29206adf, R2-M2) removed the
FrameData.force_full_repaint field and changed the PaneSurface bincode
shape. The shell.surface.v1 client-shell endpoint codec never compared
PROTOCOL_VERSION at all - only endpoint_protocol_generation and
capability names - so a 0.49.3+ client attaching to an already-running
<=0.49.2 server would silently misdecode every PaneSurface frame
instead of failing the handshake with a clear error. This also broke
docs/next/install.mdx's promise that the client shows a notice and the
old server just needs one final stop.
validate_running_server_compatibility (src/server/autodetect.rs) now
also refuses when the running server's reported protocol is below the
new MIN_COMPATIBLE_SERVER_PROTOCOL = 26 constant, reusing the existing
'needs one final server update' error path/message with a new
requirement string ('the PaneSurface wire encoding changed'). A
missing protocol (None) keeps its pre-existing permissive behavior,
matching every other capability check in this function - extracted as
server_protocol_too_old() and unit-tested directly for 25 (refused),
26 (accepted) and None (accepted, matching today).
Checked the other attach path that validates server compatibility
without going through this function: src/remote/attach.rs's
supports_endpoint_requirement (SSH remote attach) has its own
protocol field on RemoteClientStatusJson, but a pre-existing test
(saved_machine_compatibility_uses_capabilities_not_release_or_private_protocol)
proves by design that this path deliberately ignores it and gates
only on endpoint_protocol_generation/capabilities. I initially applied
the same MIN_COMPATIBLE_SERVER_PROTOCOL guard there too and it broke
that test immediately - reverted. The triage note for C88 independently
confirms remote endpoints install upstream's own herdr binary via the
SSH bridge, whose codec already matches, so the remote path's claim in
install.mdx holds without a code change; only the local same-install
case (this commit) was broken.
Verified: cargo check --all-targets clean; cargo nextest -E
'test(server::autodetect) or test(remote::attach)' -> 17/17 and 83/83
pass, including the new tests and the previously-broken-then-reverted
remote test now green again.
refs upstream sync b7781a67
* fix(remote): rename herdr --remote hints to bora --remote
cubic review finding C17(a). These four pre-existing user-facing
'herdr --remote' hints (present since before this sync, at 42b6afbd)
are the binary-rename class named in AGENTS.md 'Fork merge friction':
this fork installs and ships only bora, never herdr, so a user
following these hints verbatim would run a command that doesn't exist.
- src/remote/saved.rs:115 saved_ssh_bootstrap_command (+ its test
assertion at :194)
- src/remote/attach.rs:1292 find_installed_remote_herdr's error hint
- src/remote.rs:59 print_remote_error_hint's SSH passphrase hint
- src/remote/host.rs:57 (not in the original C17 list, included for
consistency: 'remote herdr server needs one final update... rerun
herdr --remote' is this fork's own compiled code checking its own
remote bora counterpart, not text echoed from the remote side)
Left src/remote/attach.rs:4659-4689 alone: those are
reattach_command()'s own tests, which pass a literal "herdr" *program
name* as a parametrized test input to prove the templating logic
echoes back whatever program name it's given - not an assertion that
the fork's own binary is named herdr. Left the two source-comment-only
references (config/model.rs:1191, client/handshake.rs:28) alone: Rust
doc comments aren't user-facing.
Verified: cargo check --all-targets clean; cargo nextest -E
'test(remote::saved) or test(remote::attach) or test(remote::host) or
test(src::remote)' -> 86/86 pass.
refs upstream sync b7781a67
* fix(docs): drop stale confirmation_required sentence from zh-cn/ja cli-reference
cubic review findings C38, C97. This fork has no server-side
confirmation_required response (removed in 2d839ce6; app/api/tabs.rs
closes a workspace's last tab directly). The English docs/next
cli-reference.mdx already dropped this sentence, but the merge kept
upstream's version of it in the zh-cn and ja translations, so both
described a behavior this fork doesn't have.
Deleted the trailing confirmation_required sentence from tab-close
paragraph :194 in both files so all three languages match; the
following paragraph already correctly describes the TUI confirmation
dialog for both.
Verified: python3 scripts/docs_translation_parity.py --docs-root
docs/next/website/src/content/docs -> clean, exit 0.
refs upstream sync b7781a67
* docs(agents): separate the two request_full_repaint / force_full_repaint deletions
cubic review findings C49, C91. AGENTS.md:67 conflated two different
removals as one: AppState::request_full_repaint() was deleted in
27c65c27 (the 0.9.0 client-shell rewrite, unrelated to this sync), but
FrameData.force_full_repaint (the wire-protocol field with the same
name pattern) was a separate deletion, done in this sync's own
29206adf commit (the PROTOCOL_VERSION 25->26 bump). The bullet's
wording made it look like one commit removed both.
Also AGENTS.md:8: 'Stable/preview both build from master' was stale -
this fork's default branch is main (the merge already fixed the
identical claim at :806 in the Global Contracts section, but missed
this earlier copy).
refs upstream sync b7781a67
* fix(skills): derive pre-release-audit base tag from origin/main, not stale origin/master
cubic review finding C33. origin/master is a stale 2026-08-11 branch
with no distribution/latest.json in this fork's remotes; the step
would fail outright (git show errors) before this sync brought
herdrdev/herdr#4241's audit-skill machine…
Issue
Running
herdr pane move ... --focusmoves the pane but leaves attached clients on the source workspace. The next client interaction can restore server focus to that old location.Problem
The server updates its own focus after moving the pane. Public request handling does not copy that destination to attached clients, unlike other public focus commands.
How did we fix it?
Herdr now moves every attached shell client to the destination after a successful, changed pane move requesting focus. It also reconciles locations removed by the move, even when workspace and tab indices stay unchanged. Failed moves, unchanged moves, and
--no-focusdo not trigger this focus update.Verification
Before the fix, a regression test observed server focus on
w2while the client remained onw1. Afterward, the client snapshot selects the moved pane and subsequent input reaches its terminal without reverting focus. Tests also cover multiple clients, source removal, unchanged indices, and focus guards.just checkpasses on the refreshed upstream base: 3,584 Rust tests, Windows lint, maintenance, integration, and docs checks.refs #4153