Skip to content

fix(perf): eliminate redundant terminal wake work - #2962

Merged
ogulcancelik merged 3 commits into
masterfrom
fix/terminal-wake-performance
Aug 19, 2026
Merged

fix(perf): eliminate redundant terminal wake work#2962
ogulcancelik merged 3 commits into
masterfrom
fix/terminal-wake-performance

Conversation

@ogulcancelik

Copy link
Copy Markdown
Collaborator

Summary

  • coalesce hidden-pane PTY notifications while preserving immediate visible and direct-observer wakes
  • replace aggregate terminal input-state reads with narrow scalar queries, including a tracked libghostty-vt modifyOtherKeys getter
  • add architecture enforcement and a fast stable-vs-candidate release performance smoke
  • fold the unpublished v0.8.1 notes back into the v0.8.2 unreleased changelog

Performance

  • Linux hidden50: -3.8%; visible30: -33.3%
  • macOS hidden50: -2.6%; visible30: +5.0% (+0.26 CPU points)
  • hidden-pane loop wake rate restored from about 2,800/s to about 120/s

Validation

  • just check
  • just pre-release-check
  • Linux and macOS just bench-release-smoke
  • Windows ghostty_modify_other_keys_mode_one_preserves_shift_enter runtime test

@kangal-bot kangal-bot added the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 19, 2026
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f34191df-7c7c-4040-9ebd-aa47bce4b5ee

📥 Commits

Reviewing files that changed from the base of the PR and between d78bf69 and 33addf6.

📒 Files selected for processing (2)
  • scripts/test_ui_hot_path_architecture.py
  • src/server/headless.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change replaces aggregate terminal-state reads with direct capability queries, adjusts PTY wakeup tracking for hidden and visible panes, adds architecture checks, and introduces a cross-platform release CPU smoke benchmark.

Changes

Runtime behavior

Layer / File(s) Summary
Terminal capability contracts
src/ghostty/*, src/input/*, src/pane/*, src/terminal/runtime.rs, vendor/*
Terminal APIs expose direct input-mode and modifyOtherKeys queries. Aggregate input state is restricted to Unix and test builds.
Direct capability consumers
src/app/*, src/pane.rs, src/server/*
Input, paste, mouse, scrollback, and terminal-attach paths use narrow runtime accessors.
Visibility-aware PTY rendering
src/app/mod.rs, src/app/state.rs, src/render_signal.rs, src/server/headless.rs
Render signaling tracks immediate PTY sources and coalesces hidden-pane output wakeups.
Architecture guardrails
scripts/test_ui_hot_path_architecture.py
Architecture checks scan app and server Rust sources for aggregate terminal-state access.
Behavior and release notes
CHANGELOG.md, docs/next/CHANGELOG.md
Changelogs describe hidden-pane wakeup and input-mode synchronization fixes.

Release performance validation

Layer / File(s) Summary
Benchmark harness
scripts/release_perf_case.sh, scripts/release_perf_producer.pl
New scripts create terminal scenarios, generate output, sample CPU usage, and validate benchmark artifacts.
Baseline comparison and thresholds
scripts/release_perf_smoke.sh
The smoke test compares baseline and candidate binaries across hidden and visible scenarios and enforces CPU thresholds.
Release workflow integration
justfile, AGENTS.md
The smoke test runs during pre-release checks. Release guidance requires a 60-second rerun when results move materially or when validating performance work.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🟡 Moderate · up to 33add

The PR changes terminal wake scheduling and adds release performance gating, but the current benchmark scripts can report success for an invalid zero-CPU comparison or an interrupted run, weakening the release check; duplicated pane-visibility logic can also drift and cause missed or extra wakeups. Merge readiness needs explicit owner follow-up on these bounded risks.

Sequence Diagram(s)

sequenceDiagram
  participant AppState
  participant RenderSignal
  participant HeadlessObserver
  AppState->>RenderSignal: Set immediate application-surface pane IDs
  HeadlessObserver->>RenderSignal: Request observed and hidden PTY sources
  RenderSignal-->>HeadlessObserver: Report immediate presentation work
Loading

Possibly related PRs

  • herdrdev/herdr#2892: Both changes modify hidden-pane PTY wakeups and render scheduling.
  • herdrdev/herdr#2578: Both changes modify terminal input-state handling and modifyOtherKeys.
  • herdrdev/herdr#2554: Both changes replace aggregate input_state() reads with targeted terminal accessors.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: removing redundant terminal wake work.
Description check ✅ Passed The description directly covers the wake coalescing, terminal-state changes, benchmarks, architecture checks, and changelog updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/terminal-wake-performance

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (11)
scripts/release_perf_case.sh (4)

66-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Validate root_pane and workspace_id like the loop validates pane_id.

jq -r prints the string null when the field is missing. Line 74 checks for that case, but lines 66 and 67 do not. A malformed API response makes the failure surface later as a confusing pane run or tab create error.

♻️ Proposed change
 root_pane=$(printf '%s\n' "$panes_json" | jq -r '.result.panes[0].pane_id')
 workspace_id=$("${control_env[@]}" "$bin" workspace list | jq -r '.result.workspaces[0].workspace_id')
+[[ -n "$root_pane" && "$root_pane" != null ]] || { echo "session did not report a root pane" >&2; exit 1; }
+[[ -n "$workspace_id" && "$workspace_id" != null ]] || { echo "session did not report a workspace" >&2; exit 1; }

101-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Silence the two shellcheck findings with explicit directives.

Both hints are false positives here. Line 102 relies on deliberate word splitting of $all_pids, and the comma on line 105 belongs to the -stats value of top. Add directives so the lint output stays clean and the intent is documented.

♻️ Proposed change
 if [[ $platform == linux ]]; then
+  # shellcheck disable=SC2086 # intentional word splitting of the pid list
   pid_csv=$(printf '%s\n' $all_pids | paste -sd, -)
   LC_ALL=C pidstat -h -u -p "$pid_csv" 1 "$seconds" > "$raw"
 else
+  # shellcheck disable=SC2054 # the comma is part of the top -stats value
   top_args=(top -l $((seconds + 1)) -s 1 -stats pid,cpu,time -n 2)

Source: Linters/SAST tools


110-117: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Match the pid in the PID column instead of anywhere in the row.

mean_linux scans every field for target. Another numeric column, for example UID or the CPU-core index, can equal the pid value and cause a wrong row to be attributed. With pidstat -h, the pid is always the third field.

The sample-count assertion on line 131 catches most of these cases, so this is robustness only.

♻️ Proposed change
   awk -v target="$2" '
     /^Linux/ || /^`#/` || NF < 5 { next }
-    { found=0; for (i=1; i<=NF; i++) if ($i == target) { found=1; break }
-      if (found && $(NF-2) ~ /^[0-9]+([.][0-9]+)?$/) { sum += $(NF-2); count++ } }
+    $3 == target && $(NF-2) ~ /^[0-9]+([.][0-9]+)?$/ { sum += $(NF-2); count++ }
     END { if (!count) exit 1; printf "%.6f,%d", sum/count, count }

41-53: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Interrupted runs exit with status 0 in both benchmark scripts. Both scripts bind one cleanup function to EXIT INT TERM. On INT or TERM the handler returns after rm -rf, and the script exits with the status of the last command in cleanup, which is normally 0. An interrupted benchmark then looks like a successful one to the caller and to just pre-release-check.

  • scripts/release_perf_case.sh#L41-L53: keep trap cleanup EXIT and add trap 'cleanup; exit 130' INT and trap 'cleanup; exit 143' TERM.
  • scripts/release_perf_smoke.sh#L27-L28: apply the same split so an interrupted smoke run reports failure instead of printing no verdict and exiting 0.
scripts/release_perf_producer.pl (2)

11-20: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Clamp the schedule when the producer falls behind.

$next advances by exactly $period on every iteration. If the process is descheduled or the write blocks, $remaining becomes negative and the loop emits lines with no sleep until it catches up. The producer then competes for CPU with the process under measurement and distorts the benchmark result, which is the value the release gate compares.

Reset the schedule when the deficit exceeds one period.

♻️ Proposed change
 while (1) {
     $sequence++;
     printf "\rbench-output-%08d-%s", $sequence, $label;
     $next += $period;
-    my $remaining = $next - clock_gettime(CLOCK_MONOTONIC);
-    sleep $remaining if $remaining > 0;
+    my $now = clock_gettime(CLOCK_MONOTONIC);
+    my $remaining = $next - $now;
+    if ($remaining > 0) {
+        sleep $remaining;
+    } elsif ($remaining < -$period) {
+        $next = $now + $period;
+    }
 }

6-9: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Validate that $rate is a positive number.

Line 11 computes 1 / $rate. A non-numeric or zero $rate produces a division-by-zero fatal error or a warning-laden period. The current guard only rejects falsy values.

♻️ Proposed change
 my ($rate, $gate, $label) = `@ARGV`;
-die "usage: $0 <rate-hz> <gate-file> <label>\n" unless $rate && $gate && $label;
+die "usage: $0 <rate-hz> <gate-file> <label>\n"
+    unless defined $rate && defined $gate && defined $label && length $label;
+die "rate must be a positive number\n" unless $rate =~ /^[0-9]*\.?[0-9]+$/ && $rate > 0;
scripts/release_perf_smoke.sh (2)

31-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Resolve website/latest.json relative to the repository, not the current directory.

Line 33 reads website/latest.json from the current working directory. just bench-release-smoke runs from the repository root, so that path works. A direct invocation from any other directory fails with a jq error that does not name the missing file. script_dir is already available in this script; compute it before this block and derive the path from it.

♻️ Proposed change
+script_dir=$(cd "$(dirname "$0")" && pwd)
+repo_root=$(cd "$script_dir/.." && pwd)
+
 baseline=${HERDR_PERF_BASELINE_BIN:-}
 if [[ -z "$baseline" ]]; then
-  baseline_version=$(jq -er '.version' website/latest.json)
+  baseline_version=$(jq -er '.version' "$repo_root/website/latest.json")

Then drop the duplicate script_dir assignment on line 44.


11-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Check for curl only when the script downloads the baseline.

Line 11 requires curl on every run. When HERDR_PERF_BASELINE_BIN points at a local binary, the script never calls curl. This blocks offline runs with a local baseline for no reason.

justfile (1)

142-142: 📐 Maintainability & Code Quality | 🔵 Trivial

Note the added wall-clock cost and the network dependency of pre-release-check.

bench-release-smoke runs 8 benchmark cases. Each case creates up to 50 panes, waits HERDR_PERF_WARMUP_SECONDS, and samples for HERDR_PERF_SAMPLE_SECONDS. It also downloads the current stable binary from GitHub unless HERDR_PERF_BASELINE_BIN is set. pre-release-check therefore now needs network access and takes several minutes longer.

Consider documenting the expected duration and the HERDR_PERF_BASELINE_BIN escape hatch next to this target.

AGENTS.md (1)

61-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Name the "longer release matrix" so the instruction is actionable.

The new text tells the reader when to use "the longer release matrix" but does not say what it is. The surrounding section names concrete commands, for example just bench-render-scale. Reference the specific command or document for the longer matrix.

src/server/headless.rs (1)

4090-4136: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate the two independent pane-visibility implementations.

sync_immediate_pty_sources rebuilds pane visibility from app_surface_pane_ids() plus a manual scan of every workspace/tab for direct-terminal-target matches. pty_source_visible_to_render_targets (used later at line 751 to classify the render plan) computes the same "is this pane visible to a connected render target" rule per pane, using terminal_id_for_pane and app_surface_contains_pane.

Both derive from pty_render_targets(), but they express the visibility rule in two separate places. They agree today, but a future change to one rule (for example, a new client mode, or a change to zoom/popup semantics) can silently desynchronize the other, causing either missed wakes for genuinely visible panes or unnecessary wakes for hidden ones.

Build the immediate_pty_sources set by iterating candidate panes and calling the existing per-pane pty_source_visible_to_render_targets check (or extract a single shared helper that both call sites use), instead of maintaining two separate reconstructions of the same rule.

♻️ Illustrative direction (not a drop-in patch)
 fn sync_immediate_pty_sources(&self) {
     let (has_app_target, direct_terminal_targets) = self.pty_render_targets();
-    let mut pane_ids = if has_app_target {
-        self.app.state.app_surface_pane_ids()
-    } else {
-        HashSet::new()
-    };
-    if !direct_terminal_targets.is_empty() {
-        for workspace in &self.app.state.workspaces {
-            for tab in &workspace.tabs {
-                pane_ids.extend(tab.panes.iter().filter_map(|(&pane_id, pane)| {
-                    direct_terminal_targets
-                        .contains(pane.attached_terminal_id.as_str())
-                        .then_some(pane_id)
-                }));
-            }
-        }
-        if let Some(popup) = &self.app.state.popup_pane {
-            if direct_terminal_targets.contains(popup.terminal_id.as_str()) {
-                pane_ids.insert(popup.pane_id);
-            }
-        }
-    }
+    let pane_ids = self
+        .all_known_pane_ids()
+        .filter(|&pane_id| {
+            self.pty_source_visible_to_render_targets(pane_id, has_app_target, &direct_terminal_targets)
+        })
+        .collect();
     self.app.render_dirty.set_immediate_pty_sources(pane_ids);
 }

Also consider adding a test that connects both an App client and a TerminalAttach/TerminalObserve client at once, to exercise the combined has_app_target && !direct_terminal_targets.is_empty() branch, which no current test covers.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: af4bebeb-5035-44e9-8ca4-130f76448178

📥 Commits

Reviewing files that changed from the base of the PR and between 3667151 and 4e78166.

📒 Files selected for processing (28)
  • AGENTS.md
  • CHANGELOG.md
  • docs/next/CHANGELOG.md
  • justfile
  • scripts/release_perf_case.sh
  • scripts/release_perf_producer.pl
  • scripts/release_perf_smoke.sh
  • scripts/test_ui_hot_path_architecture.py
  • src/app/actions.rs
  • src/app/api_helpers.rs
  • src/app/input/mouse.rs
  • src/app/input/terminal.rs
  • src/app/mod.rs
  • src/app/state.rs
  • src/ghostty/bindings.rs
  • src/ghostty/mod.rs
  • src/input/mod.rs
  • src/input/model.rs
  • src/pane.rs
  • src/pane/terminal.rs
  • src/render_signal.rs
  • src/server/headless.rs
  • src/server/terminal_attach.rs
  • src/terminal/runtime.rs
  • vendor/libghostty-vt.patches.md
  • vendor/libghostty-vt/include/ghostty/vt/terminal.h
  • vendor/libghostty-vt/src/terminal/c/terminal.zig
  • vendor/patches/libghostty-vt/0002-expose-modify-other-keys-mode.patch

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread scripts/release_perf_smoke.sh
Comment thread scripts/test_ui_hot_path_architecture.py
@ogulcancelik

Copy link
Copy Markdown
Collaborator Author

Addressed the benchmark review in d78bf69: interrupt failures, API ID validation, stable PID parsing, producer rate/catch-up validation, local-baseline/off-root support, benchmark sanity checks, and concrete release guidance. I also added coverage for the combined app + direct terminal observer path.

I kept the two visibility paths separate intentionally: synchronization materializes known immediate pane sources using the active-surface fast path, while request classification must fail open for stale/unknown pane IDs. Iterating all panes through request classification on every synchronization would widen a pane-scaled hot path.

@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown

Greptile Summary

The PR reduces redundant terminal wake work and replaces aggregate terminal input-state reads with narrow mode queries.

  • Coalesces hidden-pane PTY notifications while retaining immediate presentation work for visible panes and direct observers.
  • Adds scalar Ghostty terminal-mode accessors, including a vendored modifyOtherKeys query.
  • Adds architecture checks and stable-versus-candidate release performance smoke tooling.
  • Consolidates the unpublished release notes into the current unreleased changelog.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/render_signal.rs Introduces visibility-aware PTY wake coalescing while preserving generic and terminal-title presentation work.
src/server/headless.rs Integrates immediate PTY-source tracking with app clients and direct terminal observers in the headless render loop.
src/app/state.rs Computes the popup and active-tab pane set currently presented by the app surface.
src/pane/terminal.rs Adds narrow terminal-mode queries and replaces aggregate input-state reads on hot input paths.
src/ghostty/mod.rs Exposes the vendored modifyOtherKeys state through the Rust Ghostty wrapper.
vendor/libghostty-vt/src/terminal/c/terminal.zig Adds the underlying terminal-data query for modifyOtherKeys mode.
scripts/release_perf_smoke.sh Orchestrates stable-versus-candidate CPU comparisons for visible and hidden output scenarios.
scripts/release_perf_case.sh Creates isolated benchmark sessions, gathers process CPU samples, and cleans up benchmark resources.
scripts/test_ui_hot_path_architecture.py Extends deterministic enforcement of narrow terminal-state access on multiplicative UI paths.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  PTY[PTY output] --> RS[RenderSignal coalescing]
  RS -->|visible pane or direct observer| IW[Immediate wake]
  RS -->|hidden pane only| CD[Cadence-deferred work]
  IW --> LOOP[App or headless server loop]
  CD --> LOOP
  LOOP --> FRAME[Render and stream frame]
  TERM[Ghostty terminal state] --> SQ[Scalar mode queries]
  SQ --> INPUT[Keyboard, paste, focus, mouse routing]
Loading

Reviews (2): Last reviewed commit: "test(perf): strengthen performance guard..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
scripts/test_ui_hot_path_architecture.py (1)

19-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add direct coverage for each aggregate-state rule.

The scanner tests use positional FORBIDDEN_CALLS indexes. They do not directly test keyboard_state_ansi or kitty_keyboard_state_ansi. A rule reorder or regex regression could leave these new guardrails untested. Add fixtures for each new pattern, or expose named rule constants instead of relying on tuple indexes.

src/server/headless.rs (1)

9787-9817: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert immediate presentation work for a direct terminal observer.

The test proves that the observed pane is visible to a render target. It does not prove that sync_immediate_pty_sources() made the PTY request immediate. A regression in set_immediate_pty_sources() could therefore pass this test.

Add an assertion after requesting background_pane:

Proposed test strengthening
         let hidden_pane = server.app.state.workspaces[0].tabs[0].root_pane;
         server.sync_immediate_pty_sources();
-        assert!(server.app.render_dirty.request_pty(hidden_pane));
         assert!(server.app.render_dirty.request_pty(background_pane));
+        assert!(server.has_pending_presentation_work(false, false));
+        assert!(server.app.render_dirty.request_pty(hidden_pane));

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 290586d3-026a-4a61-b9bc-f960a146a3ff

📥 Commits

Reviewing files that changed from the base of the PR and between 4e78166 and d78bf69.

📒 Files selected for processing (7)
  • AGENTS.md
  • justfile
  • scripts/release_perf_case.sh
  • scripts/release_perf_producer.pl
  • scripts/release_perf_smoke.sh
  • scripts/test_ui_hot_path_architecture.py
  • src/server/headless.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • justfile

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.

@ogulcancelik

Copy link
Copy Markdown
Collaborator Author

Addressed the final CodeRabbit test suggestions in 33addf6: every aggregate-state scanner rule now has direct coverage, and the mixed app + terminal observer test proves that the observed background source itself creates immediate presentation work.

@ogulcancelik
ogulcancelik merged commit a5c69be into master Aug 19, 2026
8 checks passed
@kangal-bot kangal-bot removed the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 19, 2026
joonhwan pushed a commit to joonhwan/herdr that referenced this pull request Aug 19, 2026
* fix(perf): eliminate redundant terminal wake work

* fix(perf): harden release benchmark gate

* test(perf): strengthen performance guardrails
jerryfane added a commit to jerryfane/herdr that referenced this pull request Aug 24, 2026
* fix: match Windows Ctrl+digit keybindings (herdrdev#2913)

refs herdrdev#2910

* fix: sync powershell process working directory (herdrdev#2879)

* fix: reap ctrl-click url openers (herdrdev#2906)

* fix: reap ctrl-click url openers

refs herdrdev#2903

* fix: scope linux opener test import

refs herdrdev#2903

* test: isolate url opener regression

refs herdrdev#2903

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(docs): link agents to config reference data

* feat(website): plugin marketplace discovery shelves — trending and new arrivals

* fix(website): raise timeout on docs release integration test for slow builders

* fix: retry Windows installer activation after transient locks (herdrdev#2921)

refs herdrdev#2916

* fix: isolate hidden pane render cadence (herdrdev#2892)

refs herdrdev#2890

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: preserve macOS IME commits in report-all mode (herdrdev#2947)

* feat: make Windows generally available

* docs: publish preview documentation

* docs: finalize 0.8.1 release notes

* release: v0.8.1

* fix(ci): publish v0.8.1 documentation

* fix: restore v0.8.0 as stable release

* fix(perf): eliminate redundant terminal wake work (herdrdev#2962)

* fix(perf): eliminate redundant terminal wake work

* fix(perf): harden release benchmark gate

* test(perf): strengthen performance guardrails

* fix(theme): keep active terminal row visible under cursor (herdrdev#2989)

refs herdrdev#2987

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(cli): exit quietly when output pipes close (herdrdev#2996)

refs herdrdev#2994

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* docs: finalize 0.8.2 release notes

* release: v0.8.2

* docs: update website manifest for v0.8.2

* docs: publish preview documentation

* fix: strip ansi sequences from tab bar status (herdrdev#3003)

* fix: strip ansi sequences from tab bar status

refs herdrdev#3001

* docs: clarify tab bar escape sequence scope

refs herdrdev#3001

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(input): preserve mouse forwarding through handoff (herdrdev#3002)

refs herdrdev#3000

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: honor mouse capture config in terminal attach (herdrdev#2995)

refs herdrdev#2992

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: synchronize plugin pane pwd (herdrdev#2985)

* fix: synchronize plugin pane pwd

refs herdrdev#2984

* refactor: isolate plugin pwd platform policy

refs herdrdev#2984

* docs: fix changelog conflict resolution

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix: add claude screen activity fallbacks

refs herdrdev#1630
refs herdrdev#2241

* fix(windows): detect cursor bundled node process (herdrdev#3034)

* fix(windows): detect cursor bundled node process

refs herdrdev#3032

* docs: clarify cursor detection scope

* fix(windows): constrain cursor runtime detection

refs herdrdev#3032

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* ci: support immutable preview releases

* chore: remove akbash maintainer authority

* test: use native path in docs parity assertion (herdrdev#3043)

* chore: pin kennel maintainer identities

* chore: keep kennel authority private

* fix(graphics): isolate oversized kitty images (herdrdev#3035)

* fix(graphics): isolate oversized kitty images

refs herdrdev#3033

* fix(graphics): budget pane image cleanup

refs herdrdev#3033

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: preserve multiline paste in terminal attach (herdrdev#3056)

refs herdrdev#3054

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: keep background Claude MCP tasks working (herdrdev#3094)

* fix: match generated characters in prefix bindings (herdrdev#3085)

refs herdrdev#3079

* fix: publish Claude MCP detection manifest

* docs: document macos keychain launch context

refs herdrdev#966

* fix: preserve focus after background worktree removal (herdrdev#3099)

* fix: copy selections before delayed mouse release (herdrdev#3102)

refs herdrdev#3100

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: clear Windows Terminal identity in panes (herdrdev#3127)

* fix(windows): preserve non-us shifted text (herdrdev#3055)

* fix(windows): preserve non-us shifted text

refs herdrdev#3045

* fix(windows): retain shifted key repeat lifecycle

refs herdrdev#3045

* feat: add per-mode theme overrides (herdrdev#2324)

* feat: add per-mode theme overrides

allow [theme.custom.light] and [theme.custom.dark] blocks so custom
overrides can differ per resolved appearance when auto_switch flips.
precedence: theme defaults, then [theme.custom], then the block for
the active mode. configs without the new blocks resolve unchanged.

refs herdrdev#837

* docs: complete per-mode theme documentation

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix(windows): add local cmd installer bootstrap (herdrdev#3133)

* fix(windows): add local cmd installer bootstrap

refs herdrdev#2751

* fix(windows): bound installer downloads

refs herdrdev#2751

* fix(api): start lifecycle subscriptions from live events (herdrdev#3134)

refs herdrdev#1270

* fix: keep omp panes working through scheduled continuations (herdrdev#3122)

refs herdrdev#2851

* fix: serve a google-eligible favicon

* fix(graphics): batch kitty image row lifecycle (herdrdev#3166)

* fix(graphics): coalesce terminal kitty placements

A Unicode-placeholder image arrives as one placement per viewport row it
covers, and after 624dfd4 the budgeted encoder emitted one placement per
frame, so every redraw painted images one row per frame.

* fix(graphics): coalesce only pure kitty re-displays

Keep pixel uploads and superseded-image deletes in a transaction of
their own: a placement joins the coalesced transaction only when its
image is uploaded and its source already maps to that image, and
nothing joins after an upload or a delete.

* fix(graphics): batch kitty image row lifecycle

---------

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>

---------

Co-authored-by: Can Celik <ogulcancelik@gmail.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Co-authored-by: akbash <akbash@herdr.dev>
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
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: Alex <a.neyman17@gmail.com>
Co-authored-by: caner-akca <94343893+caner-akca@users.noreply.github.com>
Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
yjuyjuy added a commit to yjuyjuy/herdr that referenced this pull request Aug 31, 2026
* fix: preserve claude settings formatting (herdrdev#2089)

refs herdrdev#2066

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix(client): query host cell size when the ioctl reports no pixels (herdrdev#2160)

* fix(cli): report non-UTF-8 arguments instead of panicking (herdrdev#2207)

* fix(cli): report non-UTF-8 arguments instead of panicking

* fix(cli): avoid echoing malformed arguments

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: avoid blocking on foreground cwd checks (herdrdev#2213)

refs herdrdev#2206

* fix(docs): publish only released documentation

* fix(docs): support older git in snapshot checks

* fix(input): preserve hover during extended-button drags

* fix: propagate host color scheme to pane apps (herdrdev#2214)

refs herdrdev#714

* fix(pi): restrict state reporting to tui sessions (herdrdev#2159)

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix: page keys scroll pane scrollback at zsh and REPL prompts (herdrdev#2191)

* fix: page keys scroll pane scrollback at zsh and REPL prompts

PageUp/PageDown were forwarded to the pane whenever DECCKM (application
cursor) was on, assuming only primary-screen pagers enable it, but zsh's
line editor also enables DECCKM, as do REPLs such as python3. The result
was PageUp scrolling shell history instead of Herdr scrollback.

Bracketed paste discriminates the two: it means the app accepts typed or
pasted text at a prompt, so line editors enable it and pagers do not.

* test: update page key state fixture

---------

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* feat(website): rebuild marketing pages on new brand chassis

* docs: finalize 0.8.0 release documentation

* release: v0.8.0

* docs: update website manifest for v0.8.0

* docs: refine cpu optimization blog post

* fix(ui): keep collapsed workspace status visible (herdrdev#2239)

refs herdrdev#2216

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* docs: publish preview documentation

* feat(plugins): index marketplace manifests

* fix(docs): allow corrections to published versions

refs herdrdev#916

* fix(input): preserve shift-tab in pane automation (herdrdev#2259)

refs herdrdev#1561

* fix(ui): preserve sidebar scroll across clients (herdrdev#2280)

refs herdrdev#2255

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(website): copy heading links to clipboard

* fix: detect claude confirmation prompts

refs herdrdev#2268

* feat(ui): add distinct status indicators (herdrdev#2282)

refs herdrdev#2260

* fix(input): keep pending url clicks across host focus loss (herdrdev#2291)

Opening a URL raises the browser, which takes focus away from the host
terminal before the mouse release arrives. release_input_source(_headless)
cleared pending_url_click_sources on that focus loss, so the release was
forwarded to the pane and the agent opened the same URL again.

Clear the set in clear_input_source instead, whose only production caller
is remove_client.

refs herdrdev#2290

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>

* fix(cli): resolve pane query --current from caller (herdrdev#2298)

refs herdrdev#2297

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(input): parse default mouse reports (herdrdev#2312)

* fix(input): parse default mouse reports

refs herdrdev#2309

* fix(input): preserve split default mouse reports

refs herdrdev#2309

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(ui): search single-tab names in navigator (herdrdev#2320)

* fix(layout): return focus to the pane a split was opened from (herdrdev#2266)

Closing a focused pane handed focus to the next pane in tree order.
For a pane opened beside another one -- a plugin split, a file viewer,
any transient tool pane -- that is rarely where the user was: it lands
on some unrelated neighbour rather than the pane that opened it.

Track the pane focus came from in TileLayout and prefer it when the
focused pane closes, falling back to tree order when there is no
history, when it points at the pane being closed, or when it points at
a pane that has since gone away. The history lives in the layout, so it
can only ever name a pane in the same tab.

A one-slot history is only sound if internal focus excursions never
write it, so the tree edits that used to bounce focus around now go
through target-taking primitives instead. close_pane removes a
background pane directly, so detach_pane and take_pane_for_move stop
focus-close-refocusing. split_pane splits a target without moving
focus: the runtime split path only focuses the new pane once the spawn
succeeds, which makes a failed split a pure rollback, and the targeted
and unfocused workspace split paths stop fabricating history.
insert_pane_near now takes the focus intent, so an unfocused pane move
leaves the target tab's history alone. The layout-level focused-split
helpers become test-only; production splits all flow through the
target-taking path.

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(terminal): render halfwidth katakana voiced marks (herdrdev#2257)

Co-authored-by: oyoguhito <oyoguhito@kamatanoMacBook-Pro.local>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(ci): make issue gate structural

* fix(input): preserve modifyOtherKeys key releases (herdrdev#2303)

refs herdrdev#2302

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(config): accept retired agent panel scope (herdrdev#2295)

refs herdrdev#2292

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* feat(theme): add optional sidebar background

* docs: refresh readme and sponsors

* chore: remove project-local worktree extension

refs herdrdev#2325

* fix(website): update author X profile

* feat(windows): support remote attach to unix hosts (herdrdev#2329)

* feat(windows): support remote attach to unix hosts

* fix(remote): preserve unix bridge behavior

* fix(sidebar): highlight active agent in collapsed sidebar (herdrdev#2382)

The collapsed sidebar renders agent rows with an unconditional
Style::default().fg(p.overlay0), so the active agent is
indistinguishable from the others. The workspace list directly above
it, and render_agent_detail in the expanded sidebar, both mark the
active entry.

Reuse the existing active treatment: resolve the active entry with the
same is_active_pane call the expanded agent panel uses, then paint the
row background with surface_dim and the position number with text, the
same tokens the collapsed workspace rows already use.

Assisted-By: devx/717e5259-915f-4a7b-af07-6abe46da5b90

* ci: restore approved contributor gate

* chore: update canonical repository references

* fix(ci): repair contributor gate handling

* fix: avoid ctrl-tab escape sequences in legacy panes

refs herdrdev#2296

* fix: render OSC 4 palette overrides instead of forwarding the index (herdrdev#2162)

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix: keep plugin marketplace counts current

* fix(cli): accept --flag=value and reordered options in pane read/wait-output (herdrdev#2183)

* fix(cli): accept --flag=value and reordered options in pane read/wait-output

* test(cli): trim redundant pane parser coverage

---------

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* docs: document custom agent integrations

* fix(detect): recognize versioned Python agent wrappers (herdrdev#2188)

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(terminal): restore keyboard reporting on detach (herdrdev#2395)

refs herdrdev#2393

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* feat(copy-mode): add big-word motions (herdrdev#2270)

Co-authored-by: Ubuntu <ubuntu@ip-172-31-252-234.ca-west-1.compute.internal>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix: add positive idle detection for kiro-cli prompt (herdrdev#2301)

* fix: add positive idle detection for kiro-cli prompt

refs discussion herdrdev#982

* fix: remove manifest integration tests per reviewer request

---------

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(detect): ignore Cursor Run Everything status (herdrdev#2220)

* fix(detect): ignore Cursor Run Everything status

refs herdrdev#1763

* fix(detect): constrain Cursor approval controls

refs herdrdev#1763

* test(detect): remove cursor manifest behavior fixture

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix(theme): refresh host appearance on focus (herdrdev#2417)

* fix(theme): refresh host appearance on focus

refs herdrdev#2416

* fix(input): preserve split appearance replies

refs herdrdev#2416

* fix(input): retain fragmented appearance reports

refs herdrdev#2416

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* feat(marketplace): track star history for trending and new plugin surfaces

* chore: update website

* fix: publish stable release checksums

* fix: verify stable release checksums

* feat(website): rebuild compare page in new design with runtime positioning

* fix(remote): restore blocking bridge streams (herdrdev#2485)

refs herdrdev#2478

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(input): preserve shift in kitty alternate reports (herdrdev#2479)

refs herdrdev#2435

* fix: avoid redundant Windows recent-history snapshots (herdrdev#2474)

refs herdrdev#962

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(config): report unknown theme names (herdrdev#2488)

refs herdrdev#2452

* fix(windows): share Git Bash agent candidate scans (herdrdev#2494)

refs herdrdev#2459

* feat: support all agent integrations on windows (herdrdev#2496)

* fix: forward pane terminal bells (herdrdev#2498)

refs herdrdev#2453

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* chore: approve dhh as contributor

* feat(input): add per-pane right-click routing (herdrdev#2504)

* fix: make windows installer swaps atomic (herdrdev#2530)

refs herdrdev#2356

* feat: make outer pane borders configurable (herdrdev#2535)

* feat(graphics): add direct pane frame streaming (herdrdev#2523)

* feat(graphics): add direct pane frame streaming

* feat(graphics): expose pane visibility

* test(graphics): preserve legacy terminal image output

* fix(graphics): harden streaming lifecycle

* fix(input): preserve mouse releases outside panes

* fix(ui): reclaim scrollbar gutter in alternate screen (herdrdev#2538)

* fix(website): improve plugins page readability on dark ground

* fix(input): preserve alt-prefixed control keys (herdrdev#2543)

refs herdrdev#2514

* perf(ui): avoid full pane mode reads for scrollbars (herdrdev#2554)

* fix(client): avoid abort on terminal hangup (herdrdev#2427)

* fix(client): avoid abort on terminal hangup

refs herdrdev#2424

* test(client): gate PTY integration on unix

refs herdrdev#2424

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* feat: add direct pane resize keybindings (herdrdev#2558)

* test: guard pane-scaled render performance

refs herdrdev#2550

* feat: add configurable tab bar status (herdrdev#2586)

* feat: show a zoom indicator in the desktop tab bar

Reserve the right edge of the tab row for a ZOOM pill while the
focused pane is zoomed, matching the accent style of the mode bars.
The per-tab Z suffix stays; the pill makes the zoomed state visible
at a glance like tmux's status-right flag.

* feat: optionally show the hostname in the desktop tab bar

Add ui.tab_bar_hostname to display the machine's hostname at the right
edge of the tab row, like tmux's #h in status-right. The value resolves
where the server renders, so remote sessions show the remote host. Off
by default.

* fix: strip control characters from the tab bar hostname

* fix: hide the hostname when it would squeeze out the tab strip

* feat: add configurable tab bar status

* fix: harden tab bar status updates

* fix: terminate tab bar status process trees

* fix: disable status commands on unsupported platforms

* fix: skip unchanged status command renders

* fix: keep tab bar status opt-in by default

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>

* feat: add move tab keybind actions (herdrdev#2561)

* feat: add move tab keybind actions

Add optional keys.move_tab_previous/move_tab_next actions that reorder
the active tab one position, wrapping at either end. Reuses the
existing tab.move runtime path that mouse drag reordering already
drives.

* fix: exit navigate mode on single-tab move attempts

* fix: center tab labels for symmetric highlight padding (herdrdev#2570)

* fix: center tab labels for symmetric highlight padding

* docs: note centered tab labels in the changelog

* fix: center tab labels by display width, not char count

* fix(ui): anchor the host cursor to modal name inputs (herdrdev#2569)

* fix(ui): anchor the host cursor to modal name inputs

IMEs draw their composition preview at the host terminal cursor. The rename
and new-worktree dialogs drew a block glyph instead of setting one, so the
frame carried no cursor and the client kept the position the focused pane
last reported. Japanese composition appeared behind the dialog and only
reached the field on Enter.

Set the cursor to the caret column instead, counting wide characters as two
cells.

refs herdrdev#1755

* fix(ui): keep the clamped caret cell blank

A name that fills the field left the clamped caret sitting on the last
rendered glyph. A terminal inverts the cell under its cursor and an IME
composes there, so the glyph was hidden and composition overlapped it.

Render the text one column short of the field. The field is still cleared
in full, so the clamped caret always lands on a blank cell.

refs herdrdev#1755

---------

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix: track opencode tui session selection (herdrdev#2455)

refs herdrdev#2450

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* test(windows): serialize media player environment access (herdrdev#2619)

* fix(server): prioritize shutdown requests (herdrdev#2624)

refs herdrdev#2612

* fix(windows): strip elevated terminal title decoration (herdrdev#2632)

* ci: remove automatic issue gate

* fix(input): ignore modified j/k/up/down in session navigator (herdrdev#2377)

The Char('j')/Char('k')/Down/Up match arms in handle_navigator_key
had no modifier guard, unlike every sibling arm in the same match
block, so Ctrl+K, Alt+J, etc. also moved the navigator selection.

refs herdrdev#1981

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* refactor(website): clean up homepage

* feat: keep the outer terminal window title in sync with the session (herdrdev#2627)

* feat: keep the outer terminal window title in sync with the session

Herdr emulates the terminals in its panes, so an OSC 0/OSC 2 title
written inside a pane stops at Herdr and never reaches the terminal
Herdr itself runs in. That outer title is what window managers read for
title bars, tab bars, and group bars, so it kept showing whatever the
shell or ssh happened to leave behind.

Add `ui.window_title`, rendered from {hostname}, {workspace}, {tab},
{pane}, and {terminal_title}, and push it to the foreground client
whenever it changes. It renders on the server, so {hostname} names the
machine the panes actually run on rather than the machine a thin remote
client runs on, and it is gated on a pending render so an idle loop
never pays for it.

A title is only remembered as delivered once a foreground client takes
it, so the first client to attach is written to rather than skipped.
`client.window_title.set` still wins over the configured title, and
clearing it now hands the title back to `ui.window_title` instead of
only "herdr".

* fix: deliver the outer window title to a newly attached client

ClientConnected assigns the foreground client directly rather than going
through promote_client_to_foreground, so clearing the sent-title cache
there missed the case that matters most: attaching a second terminal to a
running session. The title was usually unchanged, so the sync returned
early and the new terminal kept whatever its shell or ssh had left.

Key the cache on the client that received the title instead of relying on
every foreground assignment to invalidate it.

* perf: keep hidden pane output off the window title path

Output from a hidden or background pane sets needs_render without setting
needs_full_render, and the retained render plan then skips presentation
for it entirely. Syncing the title on needs_render meant every coalesced
hidden-output tick still formatted, sanitized, and allocated a title that
could not have changed, against the hidden-source early exit AGENTS.md
requires.

Every input to the title is app state, which always requests a full
render, so gate on that instead. The one exception is the focused pane's
own terminal title, which arrives through PTY parsing, so ask for a full
render when that changes and the configured title uses it.

* fix: only cache a window title a client writer received

* fix: carry an api set window title across a live handoff

* fix: make outer window title updates event-driven

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: wait for agent prompt readiness (herdrdev#2537)

* fix: wait for agent prompt readiness

refs herdrdev#2410

* fix: return pane updates after tab bar command

refs herdrdev#2410

---------

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* chore: refresh english documentation

* fix: derive repo name for embedded bare layouts (herdrdev#2660)

refs herdrdev#2657

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: make just build cross-platform (herdrdev#2647)

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(windows): reduce idle agent detection CPU (herdrdev#2651)

* fix(windows): reduce idle agent detection CPU

refs herdrdev#2642

* fix(windows): gate agent process discovery

refs herdrdev#2642

---------

Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>

* fix(render): compact large terminal redraws (herdrdev#2675)

refs herdrdev#2670

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: disambiguate shifted punctuation keybinds (herdrdev#2676)

refs herdrdev#2674

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix: detect claude 2.1.228 half-circle busy spinner

* fix: synchronously terminate cancelled status commands

* perf: avoid repeated Git config reads (herdrdev#2688)

refs herdrdev#2643

Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* chore: update contributor list

* fix: preserve workspace labels for root repositories (herdrdev#2727)

refs herdrdev#2594

* fix: use the running binary in agent hooks (herdrdev#2722)

* fix: strip claude title spinner frames (herdrdev#2709)

* fix: source title activity glyphs from manifests

refs herdrdev#2707

* fix: simplify manifest-driven title stripping

refs herdrdev#2707

* fix: reconcile handoff title policies

refs herdrdev#2707

* fix: strip claude title spinner frames

refs herdrdev#2707

* fix: keep claude detection manifest unchanged

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix(agent): focus Copilot before prompting (herdrdev#2734)

* fix(agent): focus Copilot before prompting

refs herdrdev#1698

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(agent): cover stale focus-reporting state

refs herdrdev#1698

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: add qwen code detection and restore (herdrdev#2743)

refs herdrdev#2730

* fix: preserve logical lines in scrollback editor (herdrdev#2735)

refs herdrdev#2733

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix(graphics): support high-dpi direct file frames (herdrdev#2680)

* fix: keep tab and sidebar clicks when the terminal reports a stray drag (herdrdev#2736)

* fix: keep tab and sidebar clicks when the terminal reports a stray drag

This works around a Ghostty bug that causes herdr tabs to be unclickable
in fullscreen mode.

Ghostty on macOS in native fullscreen emits one motion report after a
fresh press in the strip where the menu bar reveals, with the row
shifted to near the bottom of the grid. The cause is upstream: macOS
delivers one mouse event whose y is nearly a window height away from the
real pointer, and Ghostty reports it as it arrives (iTerm2 and
Terminal.app report the same clicks correctly).

Herdr treated the stray report as a real drag and lost the click two
ways: it opened a tab reorder with no drop target, making the release a
no-op, and it handed the release to the pane under the bogus
coordinates.

Now, a press on the tab bar or sidebar owns the gesture until release,
and a tab drag only starts once the pointer is over a real drop target.

refs herdrdev#796

* fix: scope chrome mouse gestures to their input source

* fix: preserve chrome drag input source

* fix: preserve concurrent chrome presses

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: wait for new pane shell readiness (herdrdev#2774)

* fix: wait for new pane shell readiness

refs herdrdev#2773

* refactor: share agent startup bounds

refs herdrdev#2773

* fix: reconcile agent names after startup timeout

refs herdrdev#2773

* fix: cover agent cleanup scheduling skew

refs herdrdev#2773

* fix: bound pane shell readiness retries

refs herdrdev#2773

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: detect all claude half-circle spinner frames (herdrdev#2762)

refs herdrdev#2760

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: reject prompts to blocked agents (herdrdev#2790)

* fix: reject prompts to blocked agents

refs herdrdev#2788

* docs: clarify agent prompt timing

refs herdrdev#2788

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* feat(docs): publish agent-readable documentation

* fix(docs): apply llms patch during website builds

* fix(docs): publish channel-aware documentation indexes

* feat(cli): guide agents to Herdr resources

* fix: keep active sidebar rows visible (herdrdev#2794)

refs herdrdev#2792

* fix: speed up alternate-screen history reads (herdrdev#2426)

refs herdrdev#2387

* fix: use verified local packages for windows updates (herdrdev#2816)

refs herdrdev#2751

* fix: detect qwen localized active states (herdrdev#2818)

refs herdrdev#2756

* fix(client): handle remote terminal hangup (herdrdev#2827)

refs herdrdev#2424

* feat: make headless terminal size configurable (herdrdev#2829)

refs herdrdev#2828

* fix(theme): keep default active rows subtle and add navigate cursor color (herdrdev#2838)

refs herdrdev#2792

* fix(theme): skip host palette queries under wsl (herdrdev#2852)

refs herdrdev#2440

* fix: keep agent skill aligned with stable releases

refs herdrdev#2847

* docs: sync localized configuration updates

* docs: publish preview documentation

* fix: match Windows Ctrl+digit keybindings (herdrdev#2913)

refs herdrdev#2910

* fix: sync powershell process working directory (herdrdev#2879)

* fix: reap ctrl-click url openers (herdrdev#2906)

* fix: reap ctrl-click url openers

refs herdrdev#2903

* fix: scope linux opener test import

refs herdrdev#2903

* test: isolate url opener regression

refs herdrdev#2903

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(docs): link agents to config reference data

* feat(website): plugin marketplace discovery shelves — trending and new arrivals

* fix(website): raise timeout on docs release integration test for slow builders

* fix: retry Windows installer activation after transient locks (herdrdev#2921)

refs herdrdev#2916

* fix: isolate hidden pane render cadence (herdrdev#2892)

refs herdrdev#2890

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: preserve macOS IME commits in report-all mode (herdrdev#2947)

* feat: make Windows generally available

* docs: publish preview documentation

* docs: finalize 0.8.1 release notes

* release: v0.8.1

* fix(ci): publish v0.8.1 documentation

* fix: restore v0.8.0 as stable release

* fix(perf): eliminate redundant terminal wake work (herdrdev#2962)

* fix(perf): eliminate redundant terminal wake work

* fix(perf): harden release benchmark gate

* test(perf): strengthen performance guardrails

* fix(theme): keep active terminal row visible under cursor (herdrdev#2989)

refs herdrdev#2987

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(cli): exit quietly when output pipes close (herdrdev#2996)

refs herdrdev#2994

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* docs: finalize 0.8.2 release notes

* release: v0.8.2

* docs: update website manifest for v0.8.2

* docs: publish preview documentation

* fix: strip ansi sequences from tab bar status (herdrdev#3003)

* fix: strip ansi sequences from tab bar status

refs herdrdev#3001

* docs: clarify tab bar escape sequence scope

refs herdrdev#3001

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(input): preserve mouse forwarding through handoff (herdrdev#3002)

refs herdrdev#3000

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: honor mouse capture config in terminal attach (herdrdev#2995)

refs herdrdev#2992

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: synchronize plugin pane pwd (herdrdev#2985)

* fix: synchronize plugin pane pwd

refs herdrdev#2984

* refactor: isolate plugin pwd platform policy

refs herdrdev#2984

* docs: fix changelog conflict resolution

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>

* fix: add claude screen activity fallbacks

refs herdrdev#1630
refs herdrdev#2241

* fix(windows): detect cursor bundled node process (herdrdev#3034)

* fix(windows): detect cursor bundled node process

refs herdrdev#3032

* docs: clarify cursor detection scope

* fix(windows): constrain cursor runtime detection

refs herdrdev#3032

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* ci: support immutable preview releases

* chore: remove akbash maintainer authority

* test: use native path in docs parity assertion (herdrdev#3043)

* chore: pin kennel maintainer identities

* chore: keep kennel authority private

* fix(graphics): isolate oversized kitty images (herdrdev#3035)

* fix(graphics): isolate oversized kitty images

refs herdrdev#3033

* fix(graphics): budget pane image cleanup

refs herdrdev#3033

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: preserve multiline paste in terminal attach (herdrdev#3056)

refs herdrdev#3054

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: keep background Claude MCP tasks working (herdrdev#3094)

* fix: match generated characters in prefix bindings (herdrdev#3085)

refs herdrdev#3079

* fix: publish Claude MCP detection manifest

* docs: document macos keychain launch context

refs herdrdev#966

* fix: preserve focus after background worktree removal (herdrdev#3099)

* fix: copy selections before delayed mouse release (herdrdev#3102)

refs herdrdev#3100

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: clear Windows Terminal identity in panes (herdrdev#3127)

* fix(windows): preserve non-us shifted text (herdrdev#3055)

* fix(windows): preserve non-us shifted text

refs herdrdev#3045

* fix(windows): retain shifted key repeat lifecycle

refs herdrdev#3045

* feat: add per-mode theme overrides (herdrdev#2324)

* feat: add per-mode theme overrides

allow [theme.custom.light] and [theme.custom.dark] blocks so custom
overrides can differ per resolved appearance when auto_switch flips.
precedence: theme defaults, then [theme.custom], then the block for
the active mode. configs without the new blocks resolve unchanged.

refs herdrdev#837

* docs: complete per-mode theme documentation

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix(windows): add local cmd installer bootstrap (herdrdev#3133)

* fix(windows): add local cmd installer bootstrap

refs herdrdev#2751

* fix(windows): bound installer downloads

refs herdrdev#2751

* fix(api): start lifecycle subscriptions from live events (herdrdev#3134)

refs herdrdev#1270

* fix: keep omp panes working through scheduled continuations (herdrdev#3122)

refs herdrdev#2851

* fix: serve a google-eligible favicon

* fix(graphics): batch kitty image row lifecycle (herdrdev#3166)

* fix(graphics): coalesce terminal kitty placements

A Unicode-placeholder image arrives as one placement per viewport row it
covers, and after 624dfd4 the budgeted encoder emitted one placement per
frame, so every redraw painted images one row per frame.

* fix(graphics): coalesce only pure kitty re-displays

Keep pixel uploads and superseded-image deletes in a transaction of
their own: a placement joins the coalesced transaction only when its
image is uploaded and its source already maps to that image, and
nothing joins after an upload or a delete.

* fix(graphics): batch kitty image row lifecycle

---------

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>

* fix: refresh shared agent manifest caches (herdrdev#3204)

refs herdrdev#2711

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: report agent explain file errors as json (herdrdev#3023)

refs herdrdev#3022

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: keep wayland clipboard copies responsive (herdrdev#3015)

* fix: keep wayland clipboard copies responsive

refs herdrdev#3014

* fix: preserve clipboard fallback after wl-copy failure

refs herdrdev#3014

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: route osc 8 file links to plugin handlers (herdrdev#2942)

refs herdrdev#2941

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* docs: move changelog curation to release prep

* fix(cli): accept reordered agent report arguments (herdrdev#2928)

refs herdrdev#2926

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: require explicit workspace group close (herdrdev#3206)

refs herdrdev#2874

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* feat(detect): add muse agent with generic Pick blocked detection (herdrdev#2489)

* feat(detect): add muse agent with generic Pick blocked detection

Add Muse agent manifests and runtime detection for idle/working/blocked
states. Blocked Pick is generic on footer 'Enter to select' so any
question title/message triggers, fixing missed waiting answer where
previous AND-gated rule required specific hint combos. Cover process
names muse, muse-bin, muse-code variants and YOLO footer.

refs #herdr

* fix(detect): tighten muse manifest and agent lookup

- manifests: change working_esc_interrupt and blocked_approval
  from whole_recent to bottom_non_empty_lines(8) to use captured
  bottom-buffer controls instead of incidental whole-pane text
- manifests: anchor idle_prompt line_regex to ^\s*⟩\s*$ so working
  chrome "⟩ ..." does not match idle
- runtime: restrict muse lookup to explicit aliases
  (muse|muse-bin|muse-code|muse-cli), remove starts_with("muse")
  fallback that misclassified museum/muse-helper/muser; add
  positive/negative identification tests
- keep website mirror identical; verified with agent_detection
  manifest check --require-website

refs herdrdev#2489
refs herdrdev#2456

* fix(detect): address mjdouglas vendor findings for muse

- manifests: widen pick_request_blocked to bottom_non_empty_lines(8) and add
  Enter to toggle for multi-select; add menu_request_blocked for user-opened
  pickers (enter confirm/esc go back/esc close/space toggle); add
  workspace_trust_blocked for Do you trust dialog; tighten blocked_approval
  to footer chrome Allow once/Always allow/Yes proceed; split idle_prompt
  into any gate for empty and typed draft and not-gate pick/menu anchors;
  change idle_status_fallback from YOLO to model \u00b7 effort \u00b7 cwd shape
- runtime: replace bare muse-bin alias with muse-bin-<version> digit check
  to match versioned launcher binary and avoid museum/muse-binary false
  positives
- cross-checked with vendor-side captures reported on PR herdrdev#2489 against
  strings in shipped muse-bin binary

refs herdrdev#2489

* fix(detect): handle path-qualified muse-bin versioned binary

lookup_agent now uses path_basename before matching so a launcher
exec with absolute argv0 like /usr/local/lib/muse/muse-bin-0.1.0-R708.1
is correctly identified. is_muse_versioned_binary also checks basename
so direct path-qualified names succeed without relying on the
argv0_argv fallback path. Prevents missed Muse detection when screen
state and sound overrides are not applied.

refs herdrdev#2489

* fix(detect): add enter save to muse menu detection

Evidence documents /theme with enter save footer which was not
matched. Add enter save to menu_request_blocked any list and to
idle_prompt not-gate (highlighted row ⟩ <choice> shares glyph) and
keep website mirror in sync. Update evidence comment counts from
four to five.

refs herdrdev#2489

* docs: add Muse to supported agents table

Muse detection (manifest, process identification, sound)
was added earlier but docs/next agents table omitted the row.
Add Muse as screen manifest / none (no integration hook,
consistent with Amp/Kiro/Maki) to en/ja/zh-cn translations.

* fix(detect): resize agent arrays for new upstream variant

Agent::ALL and SCREEN_MANIFEST_AGENTS gained an entry from master
but kept stale size annotations, breaking compilation after rebase.

refs herdrdev#2489

* docs: add muse detection entry to next changelog

refs herdrdev#2489

* docs: add muse sound override to config reference

refs herdrdev#2489

* fix(detect): prevent false muse blocked states

refs herdrdev#2489

---------

Co-authored-by: Omer Hamid Kamisli <ohkamisli@poikus.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: complete docs snapshot and asset conflict resolution

Resolve leftover merge artifacts in CI-owned trees missed by the initial
conflict pass:
- docs/preview/website ja+zh-cn socket-api.mdx: de-duplicate workspace.move_block
  paragraph auto-merged from both sides; snapshot must match the preview commit
  exactly.
- website/assets/ram.svg + favicon.svg: clear stale rename conflict markers;
  ram.svg is unchanged vs the fork's previous state and favicon.svg is a new
  upstream asset.

These files are validated by website/scripts/docs-preview.mjs check and
docs-versions.mjs check, which the Website workflow runs on PRs.

---------

Co-authored-by: akbash <akbash@herdr.dev>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
Co-authored-by: WakaTaira <50697207+WakaTaira@users.noreply.github.com>
Co-authored-by: Florian <13469873+VialFlorian@users.noreply.github.com>
Co-authored-by: Rhys <105699450+rhjoh@users.noreply.github.com>
Co-authored-by: Michael Hackner <mhackner@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: kangal-bot <285672167+kangal-bot@users.noreply.github.com>
Co-authored-by: Kataoka Katsuki <49934462+kataokatsuki@users.noreply.github.com>
Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: Jon Kinney <jonkinney@gmail.com>
Co-authored-by: Kazunari Kamata <14287197+kazunari-kamata@users.noreply.github.com>
Co-authored-by: oyoguhito <oyoguhito@kamatanoMacBook-Pro.local>
Co-authored-by: Ian Ker-Seymer <i.kerseymer@gmail.com>
Co-authored-by: Mo <77584024+hamidi-dev@users.noreply.github.com>
Co-authored-by: Kyle Corbeille <kcorbeille76@gmail.com>
Co-authored-by: Phil Larson <hello@phillarson.xyz>
Co-authored-by: JP Lew <462836+jplew@users.noreply.github.com>
Co-authored-by: Ubuntu <ubuntu@ip-172-31-252-234.ca-west-1.compute.internal>
Co-authored-by: Sam Biggins <sambiggins@gmail.com>
Co-authored-by: Jesse Zhang <j3ssezhang102@gmail.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: PISIT KOOLPLUKPOL <pisit.koolplukpol@outlook.com>
Co-authored-by: Erik Krogen <erikkrogen@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Alex <a.neyman17@gmail.com>
Co-authored-by: caner-akca <94343893+caner-akca@users.noreply.github.com>
Co-authored-by: Ömer Hamid Kamışlı <hamidkamisli@icloud.com>
Co-authored-by: Omer Hamid Kamisli <ohkamisli@poikus.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants