Skip to content

chore: sync upstream 2026-10-08 - #75

Merged
arrrrny merged 27 commits into
masterfrom
sync/fork-sync-resolution-2026-10-08
Oct 9, 2026
Merged

arrrrny merged 27 commits into
masterfrom
sync/fork-sync-resolution-2026-10-08

Conversation

@arrrrny

@arrrrny arrrrny commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Merges upstream/master (4f896118) into master (bcc91654) and resolves the 7 conflicts by hand — no -X theirs, no -s ours, per the fork-upstream-sync policy.

The branch starts from the current upstream tip — one commit newer than the 4dc23bb1 recorded in the sync-conflict comment — so the next scheduled sync is a true no-op. (The issue body is from the 2026-10-06 run; the 2026-10-08 comment raised the conflict set from 5 files to 7. All seven are resolved here.)

How each conflict was resolved

1. src/api/server.rs — keep the Ziki http-push listener, adopt the named accept thread

The fork's http-push listener (resolved_http_push_listen_addr / start_http_push_server, fail-soft) stays, started before the accept loop; upstream's new crate::thread_spawn::spawn_named("herdr-api-accept", …) wraps the accept thread. The ServerHandle keeps owning the listener handle so shutdown still handles it.

2. src/app/api/worktrees/reads.rs — bounds check + restored-worktree gate

Fork's bounds-checked self.state.workspaces.get(ws_idx) else { return Err(...) } and upstream's self.require_restored_worktree_ready(ws_idx)? both run, in that order.

3. src/client/input.rs — keep the fork's idle-flush helper, drop the stale free timeout fn

Fork's flush_unix_input_after_idle helper is kept (both call sites and the tests use it). The fork's free idle_flush_timeout_ms function is dropped: upstream moved that logic onto RawInputByteFramer::idle_flush_timeout_ms, and every merged call site already uses the framer method — keeping the free function would leave dead code referencing methods that no longer exist.

4. src/detect/mod.rs — union of the ziki detection path

identify_agent_for_process(...) (fork) still runs first and yields (agent, candidate, process.pid).

5. src/client/terminal_setup.rs — upstream's host terminal probe, fork's cfg(unix) guards

Upstream's HostTerminalProbe + query_host_escape_disambiguation() (with the ?1016$p SGR pixel-mouse query) is kept, with the fork's deliberate cfg(unix) / #[cfg(not(unix))] split (fork commit 65ef2cb9, which avoids a non-unix build regression) preserved over upstream's broader cfg(not(windows)).

6. src/pane.rs — both tracking fields, split skip conditions

Both task loops carry upstream's agent_job: AgentJobTracker::default() and the fork's last_lifecycle_authority_scan_at. The old combined skip condition splits in two: upstream's background-job skip (agent_job.in_background() && !process_exited) stays, and the fork's every-5s screen scan under lifecycle authority (SCREEN_SCAN_UNDER_LIFECYCLE_AUTHORITY_INTERVAL, driving force_refresh: lifecycle_refresh_due) runs independently.

7. src/platform/mod.rs — re-export union + test-module union

unix_common re-exports keep both sides (fork: check_config_write_target, config_file_link_count, write_existing_config; upstream: begin_cli_output, end_cli_output, forward_remote_bridge_stdio, ignore_server_hangup, local_stream_peer_description, spawn_server_signal_monitor, RemoteBridgeWake). The test module keeps the fork's two live_pane_process_group tests followed by the fork's SHELL_TEST_LOCK static.

8. Semantic conflict that merged cleanly — activate_endpoint on ServerEvent::ClientShellConnected

Not every conflict hits a marker. Fork master (PR #74) added activate_endpoint to ServerEvent::ClientShellConnected, while upstream added a new test (client_shell_requests_host_pixels_for_an_unfocused_pixel_pane, upstream herdrdev#4750) that constructs the same event in src/server/headless/tests/mod.rs. Different hunks merged cleanly, but the tree did not compile (error[E0063]: missing field activate_endpoint, tests/mod.rs:6291). Resolved by passing activate_endpoint: false at that construction site, matching the other ten sites in the file. cargo check --locked --all-targets then compiles every target.

Validation

On this host (Intel macOS 15.7.9, repo-pinned Rust 1.96.1, Zig 0.16.0, MSVC cross SDK at ~/.local/share/herdr/windows-cross/), run on the merge staged for this PR:

Stage Result
cargo fmt --check pass
cargo clippy --all-targets --locked -- -D warnings pass
cargo nextest run --locked --no-fail-fast 3902/3904 pass, 12 skipped (195 s) — the 2 failures below
just maintenance-test 196 py + 13 bun, pass
just ui-hot-path-architecture-test 6 pass
just integration-assets-test 55 pass across 3 files
just windows-lint (MSVC cross) pass (3m58s)
just docs-contract-test 7 pass
scripts/fork_owned_guard.py 68 entries intact
scripts/generated_snapshot_guard.py 69 files clean

Pre-existing local failure: server_stop_request_logs_its_caller

server_stop_request_logs_its_caller (tests/server_headless.rs:636) fails
deterministically on this host — assertion failed: wait_for_exit(&mut spawned.child, Duration::from_secs(10)).
It is not a merge regression:

  • The server's own log shows a complete, correct shutdown in ~77 ms (server shutdown initiated →
    completing server shutdown → headless server exiting → herdr exiting); only the child reap lags.

  • The test is brand new here: it arrives with upstream c4653a4f (fix: keep the server running on hangup and log why it stops herdrdev/herdr#4965) and exists on neither the
    fork's master (bcc91654) nor the merge base — this merge is its first exposure.

  • Upstream 4f896118 passes it in ~2 s on this same host.

  • Standalone pty harness (spawn herdr server with stdout/stderr on a pty whose master is never
    read — exactly what the test harness does — then send server.stop):

    Binary Behaviour after server.stop
    upstream 4f896118 exits on its own at ~1.0 s (?NEs → ZN), 0 bytes drained
    merged tree (this PR) wedged in ?NEs for 20 s+, exits the instant the master is drained (476 bytes pending)
    installed pre-merge fork binary (herdr 0.9.3, built 2026-10-08 06:43) identical wedge (472 bytes pending)
  • So the fork's exit path has always waited on the unread pty; upstream's does not. This is a
    pre-existing fork-side trait, not something the merge changed — the merge only adds the first
    test that spawns the server on a never-read pty and asserts reaping.

  • It is not generic unread-pty behaviour: a C program writing ≤900 pending bytes to an unread pty
    exits normally (only a literally full 1024-byte queue blocks, and that blocks the write, not
    the exit). A precise root-cause patch is out of scope for a sync PR; the wedged process is
    un-inspectable from userland (ps -M empty; sample and lldb refuse), and the fix belongs
    with the fork's exit path. Follow-up: harden the fork exit path against an undrained pty.

Load-sensitive failure: server_survives_hangup_and_logs_why_it_stops

server_survives_hangup_and_logs_why_it_stops fails only in the full parallel run (under 3904
concurrent tests it misses its "server shutdown initiated" log deadline) and passes in isolation
on the same binary (cargo nextest run -E 'test(server_survives_hangup_and_logs_why_it_stops)'
→ PASS [1.758s]). A local load-sensitive timing flake, not a merge artifact. Both this test
and the stop test arrive with upstream c4653a4f (herdrdev#4965); neither exists on the merge base.

Closes #72

minatoaquaMK2 and others added 26 commits October 5, 2026 13:43
…4734)

* fix: keep sidebar collapse clickable with agent scrollbars

refs herdrdev#4692

* docs: document sidebar mouse handling and regression coverage

refs herdrdev#4692
* fix: retain remote sidebar preferences across attaches

refs herdrdev#4364

* test: cover remote preference identity handoff

---------

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

Bumps the github-actions group with 3 updates: [taiki-e/install-action](https://github.com/taiki-e/install-action), [cachix/install-nix-action](https://github.com/cachix/install-nix-action) and [softprops/action-gh-release](https://github.com/softprops/action-gh-release).


Updates `taiki-e/install-action` from 2.81.9 to 2.87.22
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](taiki-e/install-action@fd2f5e3...83ac0ad)

Updates `cachix/install-nix-action` from 31.10.6 to 31.11.1
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Changelog](https://github.com/cachix/install-nix-action/blob/master/RELEASE.md)
- [Commits](cachix/install-nix-action@8aa0397...13d8dd5)

Updates `softprops/action-gh-release` from 3.0.0 to 3.0.3
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](softprops/action-gh-release@b430933...efb3536)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-version: 2.87.22
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: cachix/install-nix-action
  dependency-version: 31.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…herdrdev#4964)

* fix: keep agent sessions while the agent is suspended or backgrounded

refs herdrdev#1647
refs herdrdev#1033

* fix: drop a suspended agent's session when a new one of its kind takes over

refs herdrdev#1647
…h 3 updates (herdrdev#4955)

Bumps the cargo-dependencies group with 3 updates in the / directory: [regex](https://github.com/rust-lang/regex), [serde](https://github.com/serde-rs/serde) and [futures-util](https://github.com/rust-lang/futures-rs).


Updates `regex` from 1.12.3 to 1.13.1
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](rust-lang/regex@1.12.3...1.13.1)

Updates `serde` from 1.0.228 to 1.0.229
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](serde-rs/serde@v1.0.228...v1.0.229)

Updates `futures-util` from 0.3.33 to 0.3.34
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/main/CHANGELOG.md)
- [Commits](rust-lang/futures-rs@0.3.33...0.3.34)

---
updated-dependencies:
- dependency-name: futures-util
  dependency-version: 0.3.34
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-dependencies
- dependency-name: regex
  dependency-version: 1.13.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-dependencies
- dependency-name: serde
  dependency-version: 1.0.229
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from e18b497796c12c097a38f9edb9d0641fb99eee32 to f0d9c3887740aee45f6153b24b3a6b815192ec16.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](Swatinem/rust-cache@e18b497...f0d9c38)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-version: f0d9c3887740aee45f6153b24b3a6b815192ec16
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* fix: keep the server alive when thread spawns fail

refs herdrdev#3324

* fix: keep thread spawn fallbacks from dropping work

refs herdrdev#3324

* fix: validate agent prompts before starting the waiter

refs herdrdev#3324

* fix: retry failed startup git refresh without clients

refs herdrdev#3324

* fix: time plugin commands from their exit

refs herdrdev#3324

* fix: make plugin finish time test independent of load

refs herdrdev#3324
* test: add input transparency conformance harness

* test: cover split host replies in input conformance harness

* fix: let libghostty encode every key sent to panes

* refactor: read live keyboard modes only from libghostty

* fix: wait for split input sequences instead of typing their fragments

* refactor: drop unused keyboard protocol argument from pane key encoding

* test: fix windows-only keyboard mode tests after libghostty mode refactor

* test: measure windows key record transparency against the keyboard layout

* fix: forward windows punctuation and altgr chords that produce no character

* test: expect libghostty kitty encoding in the windows conpty probe and cover release, alternate, text and modifyOtherKeys passthrough

* ci: lint windows-only test code in the cross-platform windows check

* refactor: delete herdr's own key encoder

* test: expect xterm modifyOtherKeys 2 encoding for shifted letters in the windows gauntlet

* refactor: read windows input through one native console reader

* refactor: parse sgr mouse reports once and map pixel reports without rewriting them

* refactor: decode framed input once instead of re-framing it in the client

* fix: keep macos alt, non-us kitty keys, text releases and reply-tail recovery correct after the libghostty switch

* fix: stop waiting for host replies the terminal never sends

* fix: deliver shifted punctuation releases after shift is let go

* fix: never type super, hyper or meta chords into panes that cannot read them

refs herdrdev#4356

* test: replay reporter captures for fixed split-read and key-release issues

refs herdrdev#4856
refs herdrdev#4025
refs herdrdev#4184

* fix: forward f1, f2 and f4 from kitty keyboard hosts

refs herdrdev#4403

* fix: send pixel mouse reports to unfocused panes and only when the host supports them

refs herdrdev#4750

* fix: treat 0x08 as backspace when the terminal's erase key is ctrl+h

refs herdrdev#3244

* test: fix macos lint and keep the pixel mouse server test unix-only

* fix: deliver legacy alt+escape on macos instead of dropping it

* fix: address review findings on super+enter, held key releases and the pixel mouse probe

* test: expect super+enter to stay out of plain windows panes

* test: wait for the host pixel mouse mode instead of reading the channel once

* fix: send ctrl+shift+letter to plain shells as its control byte

* fix: encode each key under one keyboard mode snapshot

* fix: keep ime text intact and stop inventing key releases for committed text

* fix: release held key reports on focus loss and never drop held-key leases

* fix: send ctrl chords on non-latin layouts as their physical key

* fix: match non-latin ctrl chord releases after ctrl is let go

* fix: drop super chords before the windows conpty fallback in plain panes
* fix: tell fish that herdr reflows its prompt on resize

refs herdrdev#3672

* fix: match fish_handle_reflow casing exactly on windows

refs herdrdev#3672

* test: keep temp path out of fish env spawn test command

refs herdrdev#3672
@zuraffa-review

zuraffa-review Bot commented Oct 9, 2026

Copy link
Copy Markdown

Walkthrough

Note

A clean, hand-resolved upstream sync: upstream/master (4f896118) merged into fork master (bcc91654) at merge base e35f3937 — 136 files, +11,259/-3,662. All seven conflicts plus one semantic seam resolve as faithful unions: the fork's ziki detection path, http-push listener, idle-flush helper, cfg(unix) guards, pane lifecycle tracking and platform re-exports all survive intact, while upstream's named thread spawn, restored-worktree gate, framer-timeout refactor, host terminal probe and pixel-pane tests land cleanly. Guardrails are green at 3e0260d7 (68/68 fork-owned entries, 69/69 generated files, cargo fmt --check clean); the only red CI is the documented pre-existing macOS exit-path trait that a new upstream test now exercises — not a merge regression.

Merge-resolution verification (at 3e0260d7)
Conflict (per PR body) Verified result
1. src/api/server.rs ✅ Fork's fail-soft http-push listener retained (resolved_http_push_listen_addr / start_http_push_server, :127-128) and started before the accept loop; upstream's named accept thread adopted (spawn_named("herdr-api-accept", …), :138); ServerHandle still owns the listener handle.
2. src/app/api/worktrees/reads.rs ✅ Fork's bounds-checked get(ws_idx) else { return Err(...) } runs before upstream's require_restored_worktree_ready(ws_idx)? (:129); the second site keeps the fork's get + return false.
3. src/client/input.rs ✅ Fork's flush_unix_input_after_idle helper kept (:339; call sites :131, :220) and now waits on upstream's RawInputByteFramer::idle_flush_timeout_ms (:129, :216); the fork's stale free timeout fn is gone.
4. src/detect/mod.rs ✅ Fork's Agent::Ziki survives the union (variant :67, ALL :96, SCREEN_MANIFEST_AGENTS :122, label/executable :152/:188, lookup :229); upstream's identify_agent_process_in_job keeps the pid via the fork's identify_agent_for_process (:259, called :283/:291).
5. src/client/terminal_setup.rs ✅ Upstream's HostTerminalProbe + query_host_escape_disambiguation() kept (:76-86) with the fork's deliberate cfg(unix)/#[cfg(not(unix))] split preserved over upstream's broader cfg(not(windows)) (:4, :147-152).
6. src/pane.rs ✅ Both loops carry upstream's agent_job: AgentJobTracker (:991) and the fork's last_lifecycle_authority_scan_at (:992); the combined skip splits into upstream's background-job skip (:1159) and the fork's 5s screen scan (SCREEN_SCAN_UNDER_LIFECYCLE_AUTHORITY_INTERVAL, :425).
7. src/platform/mod.rs ✅ unix_common re-exports keep both sides (fork: check_config_write_target, config_file_link_count, write_existing_config; upstream: begin_cli_output, end_cli_output, forward_remote_bridge_stdio, …); test module keeps the fork's live_pane_process_group tests + SHELL_TEST_LOCK (:701).
8. Semantic — activate_endpoint ✅ Upstream's new client_shell_requests_host_pixels_for_an_unfocused_pixel_pane (src/server/headless/tests/mod.rs:6265) constructs ServerEvent::ClientShellConnected with the fork's activate_endpoint: false (:6305), matching all 11 sites in the file.
Changes
Layer / File(s) Summary
Conflict seams (7 files + 1 semantic) Hand-resolved unions — see the verification table above.
src/client/shell/tests/input_conformance.rs (+1,641), src/client/shell/tests/input.rs, src/raw_input.rs, src/client/input/windows_vti.rs Upstream input-framing arrival: new conformance suite and framer-timeout work.
src/plugin_installations.rs (+470), src/cli/plugin.rs, src/app/api/plugins/**, tests/cli/plugins.rs (+594/-378) Upstream plugin-system work.
src/pane.rs (+678/-70), src/pane/terminal.rs, src/pane/background_agent.rs, src/pane/input.rs Upstream pane / background-agent tracking work.
src/platform/windows.rs, tests/windows_server_security.rs (+262), src/platform/ssh_agent.rs Upstream Windows + ssh-agent platform work.
src/app/git_refresh.rs, src/app/agent_suspend_tests.rs, tests/session_restore.rs Upstream app-layer arrival.
src/server/headless/tests/mod.rs (+321) Headless test additions, incl. the activate_endpoint semantic seam.
distribution/*, docs snapshots Generated release surfaces — snapshot guard clean.

Estimated Review Effort: 3 (30 minutes)

Category of Change Complexity
Merge-conflict seams (7 files + activate_endpoint) 🔨 Medium
Upstream code arrival (136 files, +11,259/-3,662) ⚡ Low — reviewed upstream, spot-checked here
Guardrails / CI ⚡ Low

Pre-Merge Checks

Check Status
cargo fmt --check @ 3e0260d7 ✅ Clean (run in this review)
scripts/fork_owned_guard.py @ 3e0260d7 ✅ 68 entries intact (run in this review)
scripts/generated_snapshot_guard.py @ 3e0260d7 ✅ 69 files clean (run in this review)
Maintenance suites (fork guard, snapshot guard, sync-resolve-generated, agent-detection manifest, docs translation parity, changelog, config reference) ✅ Green
check (ubuntu-latest) / check (windows-latest) / Windows ConPTY package / validate / flake check / conventional-commits ✅ Pass
check (macos-latest) ❌ server_stop_request_logs_its_caller — pre-existing fork-side exit-path trait (documented in PR body; upstream 4f896118 passes it, fork binary wedges identically) — not a merge regression
Mergeability ✅ MERGEABLE (base master)
🧸 Poetry
🐰 Seven seams stitched paw to paw,
   the fork's own flags survive it all.
   Guardrails green, the tide complete —
   one red check that predates this feat. 🥕

@zuraffa-review zuraffa-review 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.

Clean upstream sync — zero actionable findings. Full merge-fidelity audit in the walkthrough comment. 🐰

@arrrrny
arrrrny merged commit 262e780 into master Oct 9, 2026
7 checks passed
@arrrrny

arrrrny commented Oct 9, 2026

Copy link
Copy Markdown
Owner Author

Merge task

merged as 262e780

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.

sync: upstream merge conflicts require manual resolution

6 participants