Update tree-sitter-highlight requirement from 0.24.7 to 0.25.2 - #3
Closed
dependabot[bot] wants to merge 1 commit into
Closed
Update tree-sitter-highlight requirement from 0.24.7 to 0.25.2#3dependabot[bot] wants to merge 1 commit into
dependabot[bot] wants to merge 1 commit into
Conversation
Updates the requirements on [tree-sitter-highlight](https://github.com/tree-sitter/tree-sitter) to permit the latest version. - [Release notes](https://github.com/tree-sitter/tree-sitter/releases) - [Commits](tree-sitter/tree-sitter@v0.24.7...v0.25.2) --- updated-dependencies: - dependency-name: tree-sitter-highlight dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Contributor
Author
|
Superseded by #4. |
dependabot
Bot
deleted the
dependabot/cargo/tree-sitter-highlight-0.25.2
branch
March 10, 2025 01:53
sinelaw
pushed a commit
that referenced
this pull request
Apr 9, 2026
The test `test_named_color_swatch_uses_native_ansi_color` was flaky and would occasionally time out at 180s with the selection stuck on `syntax.variable` (deep past the intended `tab_active_fg` target). Root cause: the navigation loops used bounded iteration caps (`for _ in 0..50` / `for _ in 0..80`) and waited on "any screen change" (`wait_until(|h| screen_to_string() != screen_before)`) between key presses. Plugin hooks run on a separate thread and are fire-and-forget (`thread.rs::run_hook`), so after `send_key(Down)` returns the plugin may not yet have observed the key. Meanwhile any unrelated async work (semantic-highlight timer, completion trigger, diagnostic pull, auto file poll, etc.) can flip a screen cell between the keypress and the actual selection update — satisfying the "screen changed" predicate and letting the test race ahead of the plugin. Each racing iteration sends another Down, so multiple key presses pile up and get processed as a batch, skipping past `tab_active_fg` entirely. After 80 racing iterations the selection ends up clamped at `syntax.variable` (the final visible field), and the swatch-color wait then hangs forever on a row that is no longer the intended target. This also violated CONTRIBUTING.md #3 ("No timeouts or time-sensitive tests. Use semantic waiting ... Wait indefinitely"): the 50/80 caps are implicit iteration timeouts, and the "screen changed" condition isn't a semantic state — it's an any-change heuristic. Fix: replace both navigation loops with unbounded `loop {}` blocks that wait on the *selected line's content* changing (the line containing the `▸` marker). This is a true semantic condition — the selected line's text only changes when the plugin has actually processed the Down key and moved `state.selectedIndex`, so unrelated async redraws can no longer let the test race ahead. Section expansion uses the same pattern (wait until the selected line flips from `▸> UI` to `▸▼ UI`), and the "move selection away" step before checking the non-selected swatch also waits semantically. Verified with 30 sequential runs + 8 concurrent runs + 3 full theme_editor suite runs, all green. https://claude.ai/code/session_01LWvqft9RenKnj9XnzQ7Vy4
sinelaw
pushed a commit
that referenced
this pull request
Apr 9, 2026
The test `test_named_color_swatch_uses_native_ansi_color` was flaky and would occasionally time out at 180s with the selection stuck on `syntax.variable` (deep past the intended `tab_active_fg` target). Root cause: the navigation loops used bounded iteration caps (`for _ in 0..50` / `for _ in 0..80`) and waited on "any screen change" (`wait_until(|h| screen_to_string() != screen_before)`) between key presses. Plugin hooks run on a separate thread and are fire-and-forget (`thread.rs::run_hook`), so after `send_key(Down)` returns the plugin may not yet have observed the key. Meanwhile any unrelated async work (semantic-highlight timer, completion trigger, diagnostic pull, auto file poll, etc.) can flip a screen cell between the keypress and the actual selection update — satisfying the "screen changed" predicate and letting the test race ahead of the plugin. Each racing iteration sends another Down, so multiple key presses pile up and get processed as a batch, skipping past `tab_active_fg` entirely. After 80 racing iterations the selection ends up clamped at `syntax.variable` (the final visible field), and the swatch-color wait then hangs forever on a row that is no longer the intended target. This also violated CONTRIBUTING.md #3 ("No timeouts or time-sensitive tests. Use semantic waiting ... Wait indefinitely"): the 50/80 caps are implicit iteration timeouts, and the "screen changed" condition isn't a semantic state — it's an any-change heuristic. Fix: replace both navigation loops with unbounded `loop {}` blocks that wait on the *selected line's content* changing (the line containing the `▸` marker). This is a true semantic condition — the selected line's text only changes when the plugin has actually processed the Down key and moved `state.selectedIndex`, so unrelated async redraws can no longer let the test race ahead. Section expansion uses the same pattern (wait until the selected line flips from `▸> UI` to `▸▼ UI`), and the "move selection away" step before checking the non-selected swatch also waits semantically. Verified with 30 sequential runs + 8 concurrent runs + 3 full theme_editor suite runs, all green. https://claude.ai/code/session_01LWvqft9RenKnj9XnzQ7Vy4
sinelaw
pushed a commit
that referenced
this pull request
Apr 9, 2026
The test `test_named_color_swatch_uses_native_ansi_color` was flaky and would occasionally time out at 180s with the selection stuck on `syntax.variable` (deep past the intended `tab_active_fg` target). Root cause: the navigation loops used bounded iteration caps (`for _ in 0..50` / `for _ in 0..80`) and waited on "any screen change" (`wait_until(|h| screen_to_string() != screen_before)`) between key presses. Plugin hooks run on a separate thread and are fire-and-forget (`thread.rs::run_hook`), so after `send_key(Down)` returns the plugin may not yet have observed the key. Meanwhile any unrelated async work (semantic-highlight timer, completion trigger, diagnostic pull, auto file poll, etc.) can flip a screen cell between the keypress and the actual selection update — satisfying the "screen changed" predicate and letting the test race ahead of the plugin. Each racing iteration sends another Down, so multiple key presses pile up and get processed as a batch, skipping past `tab_active_fg` entirely. After 80 racing iterations the selection ends up clamped at `syntax.variable` (the final visible field), and the swatch-color wait then hangs forever on a row that is no longer the intended target. This also violated CONTRIBUTING.md #3 ("No timeouts or time-sensitive tests. Use semantic waiting ... Wait indefinitely"): the 50/80 caps are implicit iteration timeouts, and the "screen changed" condition isn't a semantic state — it's an any-change heuristic. Fix: replace both navigation loops with unbounded `loop {}` blocks that wait on the *selected line's content* changing (the line containing the `▸` marker). This is a true semantic condition — the selected line's text only changes when the plugin has actually processed the Down key and moved `state.selectedIndex`, so unrelated async redraws can no longer let the test race ahead. Section expansion uses the same pattern (wait until the selected line flips from `▸> UI` to `▸▼ UI`), and the "move selection away" step before checking the non-selected swatch also waits semantically. Verified with 30 sequential runs + 8 concurrent runs + 3 full theme_editor suite runs, all green. https://claude.ai/code/session_01LWvqft9RenKnj9XnzQ7Vy4
sinelaw
pushed a commit
that referenced
this pull request
Apr 9, 2026
The test `test_named_color_swatch_uses_native_ansi_color` was flaky and would occasionally time out at 180s with the selection stuck on `syntax.variable` (deep past the intended `tab_active_fg` target). Root cause: the navigation loops used bounded iteration caps (`for _ in 0..50` / `for _ in 0..80`) and waited on "any screen change" (`wait_until(|h| screen_to_string() != screen_before)`) between key presses. Plugin hooks run on a separate thread and are fire-and-forget (`thread.rs::run_hook`), so after `send_key(Down)` returns the plugin may not yet have observed the key. Meanwhile any unrelated async work (semantic-highlight timer, completion trigger, diagnostic pull, auto file poll, etc.) can flip a screen cell between the keypress and the actual selection update — satisfying the "screen changed" predicate and letting the test race ahead of the plugin. Each racing iteration sends another Down, so multiple key presses pile up and get processed as a batch, skipping past `tab_active_fg` entirely. After 80 racing iterations the selection ends up clamped at `syntax.variable` (the final visible field), and the swatch-color wait then hangs forever on a row that is no longer the intended target. This also violated CONTRIBUTING.md #3 ("No timeouts or time-sensitive tests. Use semantic waiting ... Wait indefinitely"): the 50/80 caps are implicit iteration timeouts, and the "screen changed" condition isn't a semantic state — it's an any-change heuristic. Fix: replace both navigation loops with unbounded `loop {}` blocks that wait on the *selected line's content* changing (the line containing the `▸` marker). This is a true semantic condition — the selected line's text only changes when the plugin has actually processed the Down key and moved `state.selectedIndex`, so unrelated async redraws can no longer let the test race ahead. Section expansion uses the same pattern (wait until the selected line flips from `▸> UI` to `▸▼ UI`), and the "move selection away" step before checking the non-selected swatch also waits semantically. Verified with 30 sequential runs + 8 concurrent runs + 3 full theme_editor suite runs, all green. https://claude.ai/code/session_01LWvqft9RenKnj9XnzQ7Vy4
sinelaw
pushed a commit
that referenced
this pull request
Apr 22, 2026
test_review_diff_n_auto_expands_collapsed_file flaked on Windows CI because it called `render()` once after pressing `n` and then asserted on screen content. The `n` key maps to the plugin's `review_next_hunk` handler, which calls `updateMagitDisplay()` to rebuild the diff buffer asynchronously — a single render tick on slower Windows runners does not always flush that rebuild, so the screen still showed the collapsed file headers without any hunk content when the assertion ran. Per CONTRIBUTING.md guideline #3 ("No timeouts or time-sensitive tests: Use semantic waiting instead of fixed timers"), replace the render+immediate-assert pattern with `wait_until` so the test waits for the expanded hunk content to actually appear on screen.
sinelaw
pushed a commit
that referenced
this pull request
Apr 26, 2026
`scroll_single_long_line_perf.rs` asserted wall-clock duration against a fixed 12s budget — exactly the kind of "time-sensitive" test CONTRIBUTING.md rule #3 forbids: > No timeouts or time-sensitive tests: use "semantic waiting" instead > of fixed timers to ensure test stability. Wait indefinitely, don't > put timeouts inside tests (cargo nextest will timeout externally). The test was added on this branch (commit 742592d) as a guard against an O(n²) regression that pre-dated the LineWrapCache + VisualRowIndex refactor. With the cache + tier-2 index in place, the structural design prevents `apply_wrapping_transform` from running uncached on every scroll tick — there's no path that can re-introduce that specific O(n²) shape. The wall-clock assertion has been racing CI hardware, and producing flaky failures (CI: 17.6s vs 12s budget; same test passes locally at ~11s) without the failure pointing at any real regression. Also picks up trivial `cargo fmt` adjustments to the four files touched by the recent VisualRowIndex work.
sinelaw
pushed a commit
that referenced
this pull request
Apr 26, 2026
`scroll_single_long_line_perf.rs` asserted wall-clock duration against a fixed 12s budget — exactly the kind of "time-sensitive" test CONTRIBUTING.md rule #3 forbids: > No timeouts or time-sensitive tests: use "semantic waiting" instead > of fixed timers to ensure test stability. Wait indefinitely, don't > put timeouts inside tests (cargo nextest will timeout externally). The test was added on this branch (commit 742592d) as a guard against an O(n²) regression that pre-dated the LineWrapCache + VisualRowIndex refactor. With the cache + tier-2 index in place, the structural design prevents `apply_wrapping_transform` from running uncached on every scroll tick — there's no path that can re-introduce that specific O(n²) shape. The wall-clock assertion has been racing CI hardware, and producing flaky failures (CI: 17.6s vs 12s budget; same test passes locally at ~11s) without the failure pointing at any real regression. Also picks up trivial `cargo fmt` adjustments to the four files touched by the recent VisualRowIndex work.
sinelaw
pushed a commit
that referenced
this pull request
Apr 26, 2026
Bug #3 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L170, Critical): when the editor restarts after a Rebuild against a malformed `devcontainer.json` (or any path that re-runs plugin init with broken JSON), the plugin's old `if (findConfig()) { registerCommands(); ... }` would skip the entire `else` branch. The user lost every `Dev Container:` command, including `Open Config` — the only in-editor route back to fix the file. Recovery required killing and restarting the editor. Fix in `plugins/devcontainer.ts`: * `tryParse` now sets `configPath` even on parse failure so the recovery `Open Config` knows where the broken file lives. * New `registerRecoveryCommands()` registers a tiny set (`open_config`, `show_build_logs`) and unregisters the full-config commands. Used in the broken-config branch of plugin init. * The `findConfig() === false` branch now calls both `showParseErrorIfAny()` (Bug #2) and `registerRecoveryCommands()`, so the user sees the parse error AND has a navigable command. Test: `broken_devcontainer_json_keeps_recovery_commands_registered`. Asserts `%cmd.open_config` + `%cmd.show_build_logs` survive a plugin init against unparseable JSON, while `%cmd.rebuild` / `%cmd.attach` (which would no-op without a parsed config) are correctly absent. Bug #2's regression test (`broken_devcontainer_json_surfaces_parse_ error_in_status_bar`) still passes — the status emit and the recovery registration coexist.
sinelaw
pushed a commit
that referenced
this pull request
Apr 26, 2026
Bug #3 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L170, Critical): when the editor restarts after a Rebuild against a malformed `devcontainer.json` (or any path that re-runs plugin init with broken JSON), the plugin's old `if (findConfig()) { registerCommands(); ... }` would skip the entire `else` branch. The user lost every `Dev Container:` command, including `Open Config` — the only in-editor route back to fix the file. Recovery required killing and restarting the editor. Fix in `plugins/devcontainer.ts`: * `tryParse` now sets `configPath` even on parse failure so the recovery `Open Config` knows where the broken file lives. * New `registerRecoveryCommands()` registers a tiny set (`open_config`, `show_build_logs`) and unregisters the full-config commands. Used in the broken-config branch of plugin init. * The `findConfig() === false` branch now calls both `showParseErrorIfAny()` (Bug #2) and `registerRecoveryCommands()`, so the user sees the parse error AND has a navigable command. Test: `broken_devcontainer_json_keeps_recovery_commands_registered`. Asserts `%cmd.open_config` + `%cmd.show_build_logs` survive a plugin init against unparseable JSON, while `%cmd.rebuild` / `%cmd.attach` (which would no-op without a parsed config) are correctly absent. Bug #2's regression test (`broken_devcontainer_json_surfaces_parse_ error_in_status_bar`) still passes — the status emit and the recovery registration coexist.
sinelaw
pushed a commit
that referenced
this pull request
Apr 27, 2026
Bug #3 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L170, Critical): when the editor restarts after a Rebuild against a malformed `devcontainer.json` (or any path that re-runs plugin init with broken JSON), the plugin's old `if (findConfig()) { registerCommands(); ... }` would skip the entire `else` branch. The user lost every `Dev Container:` command, including `Open Config` — the only in-editor route back to fix the file. Recovery required killing and restarting the editor. Fix in `plugins/devcontainer.ts`: * `tryParse` now sets `configPath` even on parse failure so the recovery `Open Config` knows where the broken file lives. * New `registerRecoveryCommands()` registers a tiny set (`open_config`, `show_build_logs`) and unregisters the full-config commands. Used in the broken-config branch of plugin init. * The `findConfig() === false` branch now calls both `showParseErrorIfAny()` (Bug #2) and `registerRecoveryCommands()`, so the user sees the parse error AND has a navigable command. Test: `broken_devcontainer_json_keeps_recovery_commands_registered`. Asserts `%cmd.open_config` + `%cmd.show_build_logs` survive a plugin init against unparseable JSON, while `%cmd.rebuild` / `%cmd.attach` (which would no-op without a parsed config) are correctly absent. Bug #2's regression test (`broken_devcontainer_json_surfaces_parse_ error_in_status_bar`) still passes — the status emit and the recovery registration coexist.
sinelaw
pushed a commit
that referenced
this pull request
Apr 27, 2026
…meout Two unrelated CI flakes, both rooted in time-sensitive test infrastructure (CONTRIBUTING.md rule #3 — "Wait indefinitely, don't put timeouts inside tests"). ## `test_multiple_reconnections` — bumped TEST_TIMEOUT 2s → 30s Symptom (slow CI): "Round 2: request should succeed: Err(Timeout)". The channel's `set_request_timeout` is a SUT parameter — every request the test issues through `request_blocking` is bounded by it, including the happy-path "should succeed" requests. At 2s, a load spike that delays the python agent's startup or response by even 1s trips the assertion before the agent can respond. Bumped to 30s — "essentially infinity" for sub-millisecond agent responses on a healthy runner, while still letting the explicit intentional-timeout assertions (`silent_agent` cases) pay at most 30s once. Tests with multiple back-to-back intentional timeouts (`test_multiple_reconnections`'s 3 rounds) run on the order of `TEST_TIMEOUT × N` wall-clock; that's bounded by nextest's external per-test timeout (default 180s) so we don't need an internal cap. ## `compose_default_width_table_scrollbar_drag` — added buffer-scrub warmup Symptom (parallel CI): "default-width/table/scrollbar-drag] 2 of 3 (width, height) combo(s) failed" at w=60 / w=100, with viewport stuck mid-table. Root cause: the markdown_compose plugin processes `lines_changed` **reactively** for the currently-visible window only. Off-screen lines have no plugin soft breaks / virtual borders in `state.soft_breaks` / `state.virtual_texts` until the user scrolls that region into view. Scroll math (`scrollbar_math::ensure_index` → `VisualRowIndex`) reads from those structures, so an unprocessed off-screen region undercounts `max_scroll_row` and scrollbar drag stops short of the buffer's tail. Mouse wheel and PageDown have a per-step `apply_visual_scroll_limit` re-clamp that masks the same under-count for those mechanisms; scrollbar drag has no such re-clamp. The previous setup waited for `wait_until_stable` on the **visible-region** condition only (no `**` markers in screen content). Under parallel test load, this returned before the plugin had processed off-screen lines. Fix: setup now does a Ctrl+End → wait_until_stable → Ctrl+Home → wait_until_stable buffer scrub. Each jump brings a different window into view, so the plugin processes both ends of the buffer (and any markdown structure within `viewport_height` of either end). The fixtures have the marker line near EOF, so a single Ctrl+End brings it through the visible window once and the plugin populates `state.soft_breaks` / `state.virtual_texts` for the table region. Subsequent scroll math has accurate counts. Both waits use semantic conditions (CONTRIBUTING.md rule #3) — no fixed timers, no time-sensitive assertions. The warmup itself is bounded only by nextest's external timeout. Note: this is a test-side fix. In production, a user opening a large markdown file, toggling compose, and immediately dragging the scrollbar to the bottom will see the same undershoot until they've scrolled around enough for the plugin to process every line. A proper production fix (eager whole-buffer `lines_changed` on compose toggle, gated to compose-mode buffers to avoid the `large.rs` memory regression) was drafted on this branch's earlier history but rolled back along with the perf rebase upstream; reintroducing it cleanly is follow-up work. ## Verification * `markdown_compose_scroll_reach::*`: 20 passed, 0 failed, 3 ignored — twice in a row in parallel mode. * `remote_channel_timeout_tests::test_multiple_reconnections`: passes in 90s wall (3 rounds × 30s timeout assertion = 90s, well within nextest's 180s budget). https://claude.ai/code/session_01CmCEFyuNawokswQUTQzAJa
sinelaw
pushed a commit
that referenced
this pull request
Apr 27, 2026
… git_log_split_tab_focus
Two unrelated test stability fixes.
## R1 lifecycle reproducer — barrier instead of wall-clock
`devcontainer_spec_conformance::lifecycle_object_form_must_run_in_parallel`
asserted parallelism via wall-clock measurement (sequential lower
bound 1.3s, parallel upper bound ~0.5s, threshold 1.1s). Under CI
load the parallel path could exceed 1.1s due to docker-exec
overhead, surfacing as
R1 (failing on master): postCreateCommand object form should
run entries in parallel. Wall clock = 1.117s > 1.1s
This violates CONTRIBUTING.md rule #3 ("No timeouts or
time-sensitive tests"). Replaced with a 3-entry **barrier**:
1. each entry touches its own `start_X` sentinel
2. each entry waits up to 3s for the OTHER two `start_*`
sentinels to exist
3. only after both observed, each entry touches its `done_X`
If the plugin runs entries in parallel, all 3 starts appear within
ms, every entry observes the others and touches its done. If the
plugin runs entries sequentially the first entry's barrier wait
can never succeed (the next entry can't start until the first
finishes — chicken-and-egg), so it exhausts its retry budget and
exits 1 without touching `done_a`. Test asserts all three
`done_X` exist; in the sequential failure mode it fails fast with
"entry `b` never satisfied the barrier — implies sequential
execution" instead of a wall-clock comparison.
Same intent as before — fails on master where the plugin runs
`postCreateCommand` entries in a sequential `for` loop, will pass
once the plugin uses `Promise.all`. The new design has no wall-
clock measurement, no fixed timer in the test (only a script-side
retry loop), and is bounded only by nextest's external per-test
timeout.
## git_log_split_tab_focus — diagnostic instrumentation
`clicking_group_tab_activates_group_in_the_clicked_split` has been
seen flaking with a 180s nextest timeout in CI. Added the
standard pair used by other tests in this directory:
init_tracing_from_env();
fresh::services::signal_handler::install_signal_handlers();
so the next CI hang dumps tracing breadcrumbs (RUST_LOG=info to
expand) plus a backtrace on SIGABRT/SIGSEGV instead of producing
a bare timeout line. This is debugging instrumentation only — it
does not attempt to fix the underlying flake (per request).
https://claude.ai/code/session_01CmCEFyuNawokswQUTQzAJa
sinelaw
pushed a commit
that referenced
this pull request
Apr 27, 2026
…meout Two unrelated CI flakes, both rooted in time-sensitive test infrastructure (CONTRIBUTING.md rule #3 — "Wait indefinitely, don't put timeouts inside tests"). ## `test_multiple_reconnections` — bumped TEST_TIMEOUT 2s → 30s Symptom (slow CI): "Round 2: request should succeed: Err(Timeout)". The channel's `set_request_timeout` is a SUT parameter — every request the test issues through `request_blocking` is bounded by it, including the happy-path "should succeed" requests. At 2s, a load spike that delays the python agent's startup or response by even 1s trips the assertion before the agent can respond. Bumped to 30s — "essentially infinity" for sub-millisecond agent responses on a healthy runner, while still letting the explicit intentional-timeout assertions (`silent_agent` cases) pay at most 30s once. Tests with multiple back-to-back intentional timeouts (`test_multiple_reconnections`'s 3 rounds) run on the order of `TEST_TIMEOUT × N` wall-clock; that's bounded by nextest's external per-test timeout (default 180s) so we don't need an internal cap. ## `compose_default_width_table_scrollbar_drag` — added buffer-scrub warmup Symptom (parallel CI): "default-width/table/scrollbar-drag] 2 of 3 (width, height) combo(s) failed" at w=60 / w=100, with viewport stuck mid-table. Root cause: the markdown_compose plugin processes `lines_changed` **reactively** for the currently-visible window only. Off-screen lines have no plugin soft breaks / virtual borders in `state.soft_breaks` / `state.virtual_texts` until the user scrolls that region into view. Scroll math (`scrollbar_math::ensure_index` → `VisualRowIndex`) reads from those structures, so an unprocessed off-screen region undercounts `max_scroll_row` and scrollbar drag stops short of the buffer's tail. Mouse wheel and PageDown have a per-step `apply_visual_scroll_limit` re-clamp that masks the same under-count for those mechanisms; scrollbar drag has no such re-clamp. The previous setup waited for `wait_until_stable` on the **visible-region** condition only (no `**` markers in screen content). Under parallel test load, this returned before the plugin had processed off-screen lines. Fix: setup now does a Ctrl+End → wait_until_stable → Ctrl+Home → wait_until_stable buffer scrub. Each jump brings a different window into view, so the plugin processes both ends of the buffer (and any markdown structure within `viewport_height` of either end). The fixtures have the marker line near EOF, so a single Ctrl+End brings it through the visible window once and the plugin populates `state.soft_breaks` / `state.virtual_texts` for the table region. Subsequent scroll math has accurate counts. Both waits use semantic conditions (CONTRIBUTING.md rule #3) — no fixed timers, no time-sensitive assertions. The warmup itself is bounded only by nextest's external timeout. Note: this is a test-side fix. In production, a user opening a large markdown file, toggling compose, and immediately dragging the scrollbar to the bottom will see the same undershoot until they've scrolled around enough for the plugin to process every line. A proper production fix (eager whole-buffer `lines_changed` on compose toggle, gated to compose-mode buffers to avoid the `large.rs` memory regression) was drafted on this branch's earlier history but rolled back along with the perf rebase upstream; reintroducing it cleanly is follow-up work. ## Verification * `markdown_compose_scroll_reach::*`: 20 passed, 0 failed, 3 ignored — twice in a row in parallel mode. * `remote_channel_timeout_tests::test_multiple_reconnections`: passes in 90s wall (3 rounds × 30s timeout assertion = 90s, well within nextest's 180s budget). https://claude.ai/code/session_01CmCEFyuNawokswQUTQzAJa
sinelaw
pushed a commit
that referenced
this pull request
Apr 27, 2026
… git_log_split_tab_focus
Two unrelated test stability fixes.
## R1 lifecycle reproducer — barrier instead of wall-clock
`devcontainer_spec_conformance::lifecycle_object_form_must_run_in_parallel`
asserted parallelism via wall-clock measurement (sequential lower
bound 1.3s, parallel upper bound ~0.5s, threshold 1.1s). Under CI
load the parallel path could exceed 1.1s due to docker-exec
overhead, surfacing as
R1 (failing on master): postCreateCommand object form should
run entries in parallel. Wall clock = 1.117s > 1.1s
This violates CONTRIBUTING.md rule #3 ("No timeouts or
time-sensitive tests"). Replaced with a 3-entry **barrier**:
1. each entry touches its own `start_X` sentinel
2. each entry waits up to 3s for the OTHER two `start_*`
sentinels to exist
3. only after both observed, each entry touches its `done_X`
If the plugin runs entries in parallel, all 3 starts appear within
ms, every entry observes the others and touches its done. If the
plugin runs entries sequentially the first entry's barrier wait
can never succeed (the next entry can't start until the first
finishes — chicken-and-egg), so it exhausts its retry budget and
exits 1 without touching `done_a`. Test asserts all three
`done_X` exist; in the sequential failure mode it fails fast with
"entry `b` never satisfied the barrier — implies sequential
execution" instead of a wall-clock comparison.
Same intent as before — fails on master where the plugin runs
`postCreateCommand` entries in a sequential `for` loop, will pass
once the plugin uses `Promise.all`. The new design has no wall-
clock measurement, no fixed timer in the test (only a script-side
retry loop), and is bounded only by nextest's external per-test
timeout.
## git_log_split_tab_focus — diagnostic instrumentation
`clicking_group_tab_activates_group_in_the_clicked_split` has been
seen flaking with a 180s nextest timeout in CI. Added the
standard pair used by other tests in this directory:
init_tracing_from_env();
fresh::services::signal_handler::install_signal_handlers();
so the next CI hang dumps tracing breadcrumbs (RUST_LOG=info to
expand) plus a backtrace on SIGABRT/SIGSEGV instead of producing
a bare timeout line. This is debugging instrumentation only — it
does not attempt to fix the underlying flake (per request).
https://claude.ai/code/session_01CmCEFyuNawokswQUTQzAJa
sinelaw
pushed a commit
that referenced
this pull request
Apr 27, 2026
…meout Two unrelated CI flakes, both rooted in time-sensitive test infrastructure (CONTRIBUTING.md rule #3 — "Wait indefinitely, don't put timeouts inside tests"). ## `test_multiple_reconnections` — bumped TEST_TIMEOUT 2s → 30s Symptom (slow CI): "Round 2: request should succeed: Err(Timeout)". The channel's `set_request_timeout` is a SUT parameter — every request the test issues through `request_blocking` is bounded by it, including the happy-path "should succeed" requests. At 2s, a load spike that delays the python agent's startup or response by even 1s trips the assertion before the agent can respond. Bumped to 30s — "essentially infinity" for sub-millisecond agent responses on a healthy runner, while still letting the explicit intentional-timeout assertions (`silent_agent` cases) pay at most 30s once. Tests with multiple back-to-back intentional timeouts (`test_multiple_reconnections`'s 3 rounds) run on the order of `TEST_TIMEOUT × N` wall-clock; that's bounded by nextest's external per-test timeout (default 180s) so we don't need an internal cap. ## `compose_default_width_table_scrollbar_drag` — added buffer-scrub warmup Symptom (parallel CI): "default-width/table/scrollbar-drag] 2 of 3 (width, height) combo(s) failed" at w=60 / w=100, with viewport stuck mid-table. Root cause: the markdown_compose plugin processes `lines_changed` **reactively** for the currently-visible window only. Off-screen lines have no plugin soft breaks / virtual borders in `state.soft_breaks` / `state.virtual_texts` until the user scrolls that region into view. Scroll math (`scrollbar_math::ensure_index` → `VisualRowIndex`) reads from those structures, so an unprocessed off-screen region undercounts `max_scroll_row` and scrollbar drag stops short of the buffer's tail. Mouse wheel and PageDown have a per-step `apply_visual_scroll_limit` re-clamp that masks the same under-count for those mechanisms; scrollbar drag has no such re-clamp. The previous setup waited for `wait_until_stable` on the **visible-region** condition only (no `**` markers in screen content). Under parallel test load, this returned before the plugin had processed off-screen lines. Fix: setup now does a Ctrl+End → wait_until_stable → Ctrl+Home → wait_until_stable buffer scrub. Each jump brings a different window into view, so the plugin processes both ends of the buffer (and any markdown structure within `viewport_height` of either end). The fixtures have the marker line near EOF, so a single Ctrl+End brings it through the visible window once and the plugin populates `state.soft_breaks` / `state.virtual_texts` for the table region. Subsequent scroll math has accurate counts. Both waits use semantic conditions (CONTRIBUTING.md rule #3) — no fixed timers, no time-sensitive assertions. The warmup itself is bounded only by nextest's external timeout. Note: this is a test-side fix. In production, a user opening a large markdown file, toggling compose, and immediately dragging the scrollbar to the bottom will see the same undershoot until they've scrolled around enough for the plugin to process every line. A proper production fix (eager whole-buffer `lines_changed` on compose toggle, gated to compose-mode buffers to avoid the `large.rs` memory regression) was drafted on this branch's earlier history but rolled back along with the perf rebase upstream; reintroducing it cleanly is follow-up work. ## Verification * `markdown_compose_scroll_reach::*`: 20 passed, 0 failed, 3 ignored — twice in a row in parallel mode. * `remote_channel_timeout_tests::test_multiple_reconnections`: passes in 90s wall (3 rounds × 30s timeout assertion = 90s, well within nextest's 180s budget). https://claude.ai/code/session_01CmCEFyuNawokswQUTQzAJa
sinelaw
pushed a commit
that referenced
this pull request
Apr 27, 2026
… git_log_split_tab_focus
Two unrelated test stability fixes.
## R1 lifecycle reproducer — barrier instead of wall-clock
`devcontainer_spec_conformance::lifecycle_object_form_must_run_in_parallel`
asserted parallelism via wall-clock measurement (sequential lower
bound 1.3s, parallel upper bound ~0.5s, threshold 1.1s). Under CI
load the parallel path could exceed 1.1s due to docker-exec
overhead, surfacing as
R1 (failing on master): postCreateCommand object form should
run entries in parallel. Wall clock = 1.117s > 1.1s
This violates CONTRIBUTING.md rule #3 ("No timeouts or
time-sensitive tests"). Replaced with a 3-entry **barrier**:
1. each entry touches its own `start_X` sentinel
2. each entry waits up to 3s for the OTHER two `start_*`
sentinels to exist
3. only after both observed, each entry touches its `done_X`
If the plugin runs entries in parallel, all 3 starts appear within
ms, every entry observes the others and touches its done. If the
plugin runs entries sequentially the first entry's barrier wait
can never succeed (the next entry can't start until the first
finishes — chicken-and-egg), so it exhausts its retry budget and
exits 1 without touching `done_a`. Test asserts all three
`done_X` exist; in the sequential failure mode it fails fast with
"entry `b` never satisfied the barrier — implies sequential
execution" instead of a wall-clock comparison.
Same intent as before — fails on master where the plugin runs
`postCreateCommand` entries in a sequential `for` loop, will pass
once the plugin uses `Promise.all`. The new design has no wall-
clock measurement, no fixed timer in the test (only a script-side
retry loop), and is bounded only by nextest's external per-test
timeout.
## git_log_split_tab_focus — diagnostic instrumentation
`clicking_group_tab_activates_group_in_the_clicked_split` has been
seen flaking with a 180s nextest timeout in CI. Added the
standard pair used by other tests in this directory:
init_tracing_from_env();
fresh::services::signal_handler::install_signal_handlers();
so the next CI hang dumps tracing breadcrumbs (RUST_LOG=info to
expand) plus a backtrace on SIGABRT/SIGSEGV instead of producing
a bare timeout line. This is debugging instrumentation only — it
does not attempt to fix the underlying flake (per request).
https://claude.ai/code/session_01CmCEFyuNawokswQUTQzAJa
sinelaw
pushed a commit
that referenced
this pull request
Apr 28, 2026
… don't race The flash plugin's main loop updated `state.pattern` and the `Flash[<pattern>]` status banner inside the keypress handler, BEFORE `continue`ing back to the top of the loop where the next iteration recomputes labels and redraws conceals. The banner therefore reached the screen ahead of the matching conceals — a render tick in that window showed banner=N with conceals from iteration N-1. E2E tests (`type_pattern` in tests/e2e/flash.rs) treat the banner as a synchronization barrier: they `wait_until` the screen contains `Flash[<pattern>]` before asserting on rendered conceals. Under load on slower / Windows CI the renderer would tick between the banner update and the redraw, the test would observe banner="PID" with conceals still anchored to "PI", and `flash_label_does_not_eat_space_after_match` would fail with rendered output like `PIa file lockup` (label `a` on the `D` cell, where it would land for pattern "PI"). Fix: call `setStatusForPattern()` AFTER `redraw()` within the same loop iteration, and drop the eager calls inside the keypress handler. Now any observer that sees `Flash[<pattern>]` is guaranteed to also see the conceals/labels for that pattern. Per CONTRIBUTING rule #3 the test keeps semantic waiting (no timeouts); the fix makes its existing wait condition correct rather than introducing one.
sinelaw
pushed a commit
that referenced
this pull request
Apr 30, 2026
Two paper cuts surfaced after smoke-testing 9e302d6: 1. Quickfix and other dock-routed virtual buffers showed up as a tab in *both* the editor split and the dock leaf. Cause: create_virtual_buffer tabs the new buffer into the active split before the dispatcher re-parents it to the dock. Fix: capture the source split before create_virtual_buffer, and call source.remove_buffer(buffer_id) after set_pane_buffer(dock_leaf, ...) — same pattern the existing dispatcher uses on the cold-start branch (#3 in the comments at plugin_dispatch.rs:2090). Applied in both handle_create_virtual_buffer_in_split (utility-dock fast path) and Editor::install_quickfix_in_dock. 2. Pressing Tab inside the floating overlay clobbered the search query with the suggestion's `value` field — which the Finder library uses as an opaque index ("0", "1", …), so the input visibly became "0". Fix: in view/prompt_input.rs, short-circuit the Tab handler when prompt.overlay is true (arrows already navigate suggestions, so Tab has no other useful job in overlay mode). Verified in tmux: Alt+/ → type "split_active" → Tab leaves the input unchanged ("split_active", 1/31 still selected); Alt+Q exports to the dock and the top tab bar shows only "README.md" while the dock holds only "*Quickfix*". https://claude.ai/code/session_01FJMAjx2SQXjqsrmW1ojCXr
sinelaw
pushed a commit
that referenced
this pull request
Apr 30, 2026
…md rule #3 The file shared a single 30s `TEST_TIMEOUT` between two assertion classes that have opposite contracts: - intentional-timeout assertions need a finite timeout (that's the behavior under test); - happy-path "should succeed" assertions must, per rule #3, wait indefinitely so that CI load spikes can't flip them into spurious `Err(Timeout)`. The flake report on `test_multiple_reconnections` ("Round 3: request should succeed: Err(Timeout)") was the predictable failure mode of that conflict: a slow scheduler push during a should-succeed call exceeded the shared 30s ceiling. Fix: arm the channel's per-request timeout per call via two helpers — `arm_intentional_timeout` (2s) before deliberate-timeout assertions, `arm_happy_path` (1h, "essentially infinite") before any should-succeed assertion. nextest's external per-test cap still catches genuine hangs. 5 consecutive local runs of test_multiple_reconnections complete deterministically in 6.09s (down from a 30+ second wall-clock budget that was tripping at the 90s mark on slow runners). https://claude.ai/code/session_01H9y9oj4ZZqWnDURtJfk1u7
sinelaw
pushed a commit
that referenced
this pull request
Apr 30, 2026
…md rule #3 The file shared a single 30s `TEST_TIMEOUT` between two assertion classes that have opposite contracts: - intentional-timeout assertions need a finite timeout (that's the behavior under test); - happy-path "should succeed" assertions must, per rule #3, wait indefinitely so that CI load spikes can't flip them into spurious `Err(Timeout)`. The flake report on `test_multiple_reconnections` ("Round 3: request should succeed: Err(Timeout)") was the predictable failure mode of that conflict: a slow scheduler push during a should-succeed call exceeded the shared 30s ceiling. Fix: arm the channel's per-request timeout per call via two helpers — `arm_intentional_timeout` (2s) before deliberate-timeout assertions, `arm_happy_path` (1h, "essentially infinite") before any should-succeed assertion. nextest's external per-test cap still catches genuine hangs. 5 consecutive local runs of test_multiple_reconnections complete deterministically in 6.09s (down from a 30+ second wall-clock budget that was tripping at the 90s mark on slow runners). https://claude.ai/code/session_01H9y9oj4ZZqWnDURtJfk1u7
sinelaw
pushed a commit
that referenced
this pull request
Apr 30, 2026
Two paper cuts surfaced after smoke-testing 9e302d6: 1. Quickfix and other dock-routed virtual buffers showed up as a tab in *both* the editor split and the dock leaf. Cause: create_virtual_buffer tabs the new buffer into the active split before the dispatcher re-parents it to the dock. Fix: capture the source split before create_virtual_buffer, and call source.remove_buffer(buffer_id) after set_pane_buffer(dock_leaf, ...) — same pattern the existing dispatcher uses on the cold-start branch (#3 in the comments at plugin_dispatch.rs:2090). Applied in both handle_create_virtual_buffer_in_split (utility-dock fast path) and Editor::install_quickfix_in_dock. 2. Pressing Tab inside the floating overlay clobbered the search query with the suggestion's `value` field — which the Finder library uses as an opaque index ("0", "1", …), so the input visibly became "0". Fix: in view/prompt_input.rs, short-circuit the Tab handler when prompt.overlay is true (arrows already navigate suggestions, so Tab has no other useful job in overlay mode). Verified in tmux: Alt+/ → type "split_active" → Tab leaves the input unchanged ("split_active", 1/31 still selected); Alt+Q exports to the dock and the top tab bar shows only "README.md" while the dock holds only "*Quickfix*". https://claude.ai/code/session_01FJMAjx2SQXjqsrmW1ojCXr
sinelaw
pushed a commit
that referenced
this pull request
May 24, 2026
Re-point the issue #2056 specs from the measured (buggy) behavior to the agreed target behavior. The fix is reverted on this branch, so these are intentionally RED (TDD spec-first) until it lands. Desired behavior pinned: - `fresh <project>` activates the project-rooted window; a worktree session (root != cwd) is never activated by passing the project dir and survives as an inactive shell. - working_dir == active_window().root (no boot-time inconsistency). - cross-project: the other project's window is preserved, not dropped by an id-1 collision with the clean-base fallback. - v1 legacy migrates, then still activates a clean base at the cwd. - the file explorer roots at the ACTIVE window and re-roots on a dive (defect #3), instead of being keyed off a global working_dir and sticking to its first-init root. Red specs (7): v2_worktree_session_does_not_hijack_plain_launch, v2_cross_project_only_boots_clean_base_and_preserves_other, v2_base_and_worktree_activates_the_base, v1_legacy_percwd_migrates_then_activates_base_at_cwd, launch_in_project_roots_rendered_ui_at_project, diving_between_windows_roots_the_ui_at_the_active_window, observe_rendered_root_with_orchestrator_plugin_loaded. https://claude.ai/code/session_01R6SPCEgzj4HWZBSe2VZPZM
sinelaw
pushed a commit
that referenced
this pull request
May 24, 2026
…on notes Maps the bring-up pipeline (construct -> restore -> file-explorer init -> orchestrator) end-to-end and identifies where small data-flow changes collapse the branch/state explosion for issue #2056: - Editor.working_dir duplicates active_window().root (its own doc says so), hand-synced at 7 sites + construction; the construction site is the one that does NOT sync, which is the entire #2056 bug class. Recommend deriving working_dir from active_window().root. - init_file_explorer reads working_dir instead of the window's own root, contradicting the Window.file_explorer doc (defect #3). - the launch pick matches project_path OR root (two identities); should match root only. - clean-base fallback reuses id 1 and drops a colliding persisted window. - restore is implemented twice (first-run vs restart) and the harness mirror already drifted. Also audits single->multi-window migration leftovers: working_dir is the one straggler that should be per-window-derived; session_name is the server/attach session (unrelated to orchestrator windows), worth renaming to reduce confusion. https://claude.ai/code/session_01R6SPCEgzj4HWZBSe2VZPZM
sinelaw
pushed a commit
that referenced
this pull request
May 24, 2026
… active window Issue #2056. Two changes that green the worktree-hijack specs: - pick_active_window_for_cwd now matches a persisted window by its `root` (where the window opens), not `project_path`. An orchestrator worktree session has project_path == parent project but root == worktree, so the old project_path match resurrected a worktree-rooted window when launching `fresh <project>`. Matching on root means the active window always opens at the launch cwd; worktree sessions stay inactive shells, divable via the orchestrator. project_path remains orchestrator-dialog grouping metadata only. - init_file_explorer roots the (per-window) tree at the active window's own `root` instead of the global working_dir (defect #3), so the explorer always reflects the window it belongs to and follows a dive. Greens 6 of the 7 #2056 specs; the cross-project id-1 collision case is handled separately (it needs base-session id decoupling). https://claude.ai/code/session_01R6SPCEgzj4HWZBSe2VZPZM
sinelaw
pushed a commit
that referenced
this pull request
May 24, 2026
….root The file explorer is per-window state; its constructor now lives on `impl Window` and builds from `self.root` using the window's own `resources` (runtime/fs/authority) + `bridge`. A Window has no access to any other project's path, so "explorer rooted at the wrong project" is unrepresentable (issue #2056 defect #3), not merely avoided. Editor::init_file_explorer is now a thin delegator to `active_window_mut().init_file_explorer()`; callers unchanged. cargo check + fmt clean. https://claude.ai/code/session_01R6SPCEgzj4HWZBSe2VZPZM
sinelaw
pushed a commit
that referenced
this pull request
May 26, 2026
…e complete Tested: TC-025 (Save As), TC-027-029 (tabs), TC-034 (cut), TC-036 (block select), TC-037 (comments), TC-038 (auto-indent), TC-043 (Find Prev), TC-048-049 (search toggles), TC-055 (file explorer), TC-056-058 (line nums/wrap/terminal), command palette coverage (TC-062/065), and BUG-006 reproduction (not reproduced ×2). Key discoveries: Ctrl+PgDn/PgUp = buffer switching; Ctrl+E = explorer focus; Save As = File menu only; Line Wrap toggle = View menu only; close-buffer prompt needs letter + Enter; preview tabs auto-open during explorer navigation. Added Run #3 lessons (8-11) to run_log, updated all test cases to PASSED, updated learning_db false-positive patterns and key bindings, added IMP-010/011. https://claude.ai/code/session_01W7YDfJdcwaEGvBBzsQpmBD
sinelaw
pushed a commit
that referenced
this pull request
May 27, 2026
A mouse click on a session row fires `select` with the clicked item's key (not the list key "sessions"), so the handler ignored it. Accept the click by also matching the payload's `list_key`, so clicking a row selects + live-switches it (and the focus event re-arms the dock). Add e2e: "/"+filter+Enter-returns-to-list (#5/#6), Space multi-select (#4), and mouse-click-row-then-Space-checks-that-row (#3). Make the order-stability test deterministic (sibling project dirs under one parent so the project-path sort is fixed) — the earlier flake was random tempdir paths, not the dock.
sinelaw
pushed a commit
that referenced
this pull request
May 27, 2026
A mouse click on a session row fires `select` with the clicked item's key (not the list key "sessions"), so the handler ignored it. Accept the click by also matching the payload's `list_key`, so clicking a row selects + live-switches it (and the focus event re-arms the dock). Add e2e: "/"+filter+Enter-returns-to-list (#5/#6), Space multi-select (#4), and mouse-click-row-then-Space-checks-that-row (#3). Make the order-stability test deterministic (sibling project dirs under one parent so the project-path sort is fixed) — the earlier flake was random tempdir paths, not the dock.
sinelaw
pushed a commit
that referenced
this pull request
May 31, 2026
A mouse click on a session row fires `select` with the clicked item's key (not the list key "sessions"), so the handler ignored it. Accept the click by also matching the payload's `list_key`, so clicking a row selects + live-switches it (and the focus event re-arms the dock). Add e2e: "/"+filter+Enter-returns-to-list (#5/#6), Space multi-select (#4), and mouse-click-row-then-Space-checks-that-row (#3). Make the order-stability test deterministic (sibling project dirs under one parent so the project-path sort is fixed) — the earlier flake was random tempdir paths, not the dock.
sinelaw
pushed a commit
that referenced
this pull request
Jun 2, 2026
#3: the project tag and git summary were positioned by a plugin-side width estimate (dockDefaultWidth), so a user-dragged dock width left them stranded at the old column. Lay lines 1-2 out as host rows with a flex spacer between the left group and the right tag instead — the host computes the fill at the card's real render width, so the tags stay flush-right and re-flow on resize with no estimate. Drops the now-dead justify()/contentW width math; caps the branch so a long one doesn't push the right-aligned git summary off the row tail.
sinelaw
pushed a commit
that referenced
this pull request
Jun 3, 2026
…restore
Survey of how multiplexers, terminal emulators, IDEs, process-checkpoint
tools, and agent orchestrators handle restore, each checked against a real
implementation:
1. keep process alive (tmux, iTerm2, VS Code reconnect, Superset pty-daemon
fd-handoff [verified from source], claude-squad, Fresh detach)
2. re-launch from saved command (2a bare: tmux-resurrect/zellij;
2b native resume: herdr/cmux/Conductor/VS Code revive)
3. screen snapshot only (tmux capture-pane, zellij viewport, iTerm2 OS
restore, Fresh backing-file)
4. process checkpoint/restore (CRIU/DMTCP) — rejected
5. app-level reconstruction from transcript (Conductor/Crystal/Vibe Kanban)
Maps where Fresh sits (#1 + #3 today) and why 2b is the gap to fill.
sinelaw
pushed a commit
that referenced
this pull request
Jun 8, 2026
…shift-wheel h-scroll - #2: opening a comment from the panel in side-by-side now rebuilds the composite focused on the comment's hunk (parameterized buildCenterComposite with a focus-hunk index) instead of staying at the first hunk — the unified cursor jump is inert when the composite is showing. - #3: comments rail is narrow by default (~15% width; diff/comments split 0.74 -> 0.82). - #4: Shift+mouse-wheel over the side-by-side area now pans horizontally — handle_horizontal_scroll scrolls the composite's per-pane left_column (it only touched the split viewport, which the composite render ignores). - #1: pin the FILES cursor to the selected row's start on every focus path (incl. mouse via buffer_activated), so it can't land horizontally scrolled. https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw
pushed a commit
that referenced
this pull request
Jun 8, 2026
…h-scroll (#4) - test_review_comments_rail_is_narrow: the COMMENTS header starts past column 130 of a 160-col screen (rail ~15%). - test_review_side_by_side_shift_wheel_scrolls_horizontally: Shift+wheel on the composite pans it (rendered content changes from the left-edge state). (#2 comment-jump and #1 file-focus h-scroll are verified manually in tmux; the harness can't drive the async comment-jump rebuild / the residual #1 edge reliably.) https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw
pushed a commit
that referenced
this pull request
Jun 8, 2026
…shift-wheel h-scroll - #2: opening a comment from the panel in side-by-side now rebuilds the composite focused on the comment's hunk (parameterized buildCenterComposite with a focus-hunk index) instead of staying at the first hunk — the unified cursor jump is inert when the composite is showing. - #3: comments rail is narrow by default (~15% width; diff/comments split 0.74 -> 0.82). - #4: Shift+mouse-wheel over the side-by-side area now pans horizontally — handle_horizontal_scroll scrolls the composite's per-pane left_column (it only touched the split viewport, which the composite render ignores). - #1: pin the FILES cursor to the selected row's start on every focus path (incl. mouse via buffer_activated), so it can't land horizontally scrolled. https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw
pushed a commit
that referenced
this pull request
Jun 8, 2026
…h-scroll (#4) - test_review_comments_rail_is_narrow: the COMMENTS header starts past column 130 of a 160-col screen (rail ~15%). - test_review_side_by_side_shift_wheel_scrolls_horizontally: Shift+wheel on the composite pans it (rendered content changes from the left-edge state). (#2 comment-jump and #1 file-focus h-scroll are verified manually in tmux; the harness can't drive the async comment-jump rebuild / the residual #1 edge reliably.) https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw
pushed a commit
that referenced
this pull request
Jun 22, 2026
CONTRIBUTING #3 (semantic waiting) violations in the vi e2e suite. The repeat-change-word test asserted buffer content immediately after `.`, which replays the recorded change (cW → insert "X" → Esc) through the async command pipeline in stages: WORD delete, then text insert. The bare assert could observe the intermediate "X four.five" between those stages, failing against the expected "X X four.five" (seen flaking on Windows CI). The same shape — a buffer-mutating command sent via send_vi_key followed by a bare assert_buffer_content with no wait — also appears in four paste (`p`) sites. Sibling paste/repeat tests already wait (wait_for_buffer_content / wait_for_rendered_lines_in_order); these did not. Convert all of them to wait_for_buffer_content so the assertion waits indefinitely for the operation to settle instead of racing it. Audited the rest of vi_mode.rs and vi_mode_bugs.rs: remaining assert_buffer_content calls are either preceded by mode/cursor semantic waits, assert unchanged content on no-op/read-only operations, or follow send_vi_operator_motion (which waits for the resulting mode) — not the same race. vi_mode_bugs.rs already uses wait_for_buffer_content throughout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LgNDJLwfN6MtVcAo3kLwQR
sinelaw
pushed a commit
that referenced
this pull request
Jun 22, 2026
…assert CONTRIBUTING #3 (semantic waiting) again, this time on cursor position. The paragraph-motion tests wait on cursor_position() after each `j` (send_vi_key only sends + renders; the motion lands through the async pipeline), but then asserted the result of the `}` / `{` paragraph motion with a bare assert_eq!(cursor_position(), N) — no wait — so the assert could read the pre-motion position and fail intermittently (test_vi_vim_compat_paragraph_down_at_eof_without_trailing_newline_stays_on_last_char was the reported flake). Convert all six such cursor asserts to wait_until(cursor_position() == N), matching the `j`-motion waits already used in the same tests. Left vi_mode_bugs.rs:372 alone: it asserts the *initial* cursor position (0) as a precondition before any motion, and the motion below it already uses wait_until. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LgNDJLwfN6MtVcAo3kLwQR
sinelaw
pushed a commit
that referenced
this pull request
Jun 22, 2026
CONTRIBUTING #3 (semantic waiting) violations in the vi e2e suite. The repeat-change-word test asserted buffer content immediately after `.`, which replays the recorded change (cW → insert "X" → Esc) through the async command pipeline in stages: WORD delete, then text insert. The bare assert could observe the intermediate "X four.five" between those stages, failing against the expected "X X four.five" (seen flaking on Windows CI). The same shape — a buffer-mutating command sent via send_vi_key followed by a bare assert_buffer_content with no wait — also appears in four paste (`p`) sites. Sibling paste/repeat tests already wait (wait_for_buffer_content / wait_for_rendered_lines_in_order); these did not. Convert all of them to wait_for_buffer_content so the assertion waits indefinitely for the operation to settle instead of racing it. Audited the rest of vi_mode.rs and vi_mode_bugs.rs: remaining assert_buffer_content calls are either preceded by mode/cursor semantic waits, assert unchanged content on no-op/read-only operations, or follow send_vi_operator_motion (which waits for the resulting mode) — not the same race. vi_mode_bugs.rs already uses wait_for_buffer_content throughout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LgNDJLwfN6MtVcAo3kLwQR
sinelaw
pushed a commit
that referenced
this pull request
Jun 22, 2026
…assert CONTRIBUTING #3 (semantic waiting) again, this time on cursor position. The paragraph-motion tests wait on cursor_position() after each `j` (send_vi_key only sends + renders; the motion lands through the async pipeline), but then asserted the result of the `}` / `{` paragraph motion with a bare assert_eq!(cursor_position(), N) — no wait — so the assert could read the pre-motion position and fail intermittently (test_vi_vim_compat_paragraph_down_at_eof_without_trailing_newline_stays_on_last_char was the reported flake). Convert all six such cursor asserts to wait_until(cursor_position() == N), matching the `j`-motion waits already used in the same tests. Left vi_mode_bugs.rs:372 alone: it asserts the *initial* cursor position (0) as a precondition before any motion, and the motion below it already uses wait_until. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LgNDJLwfN6MtVcAo3kLwQR
sinelaw
pushed a commit
that referenced
this pull request
Jun 24, 2026
Replaces the per-frame reconnect poller with an event-driven path and unifies the two reconnect mechanisms, and makes a plain `fresh ssh://…` launch a first-class remote session. Three connected changes: #1 Uniform backend spec. `Authority::session_spec()` derives the persistable `SessionAuthoritySpec` from the live authority's `command_wrap` (SSH/Kube → `RemoteAgent`, local/container → `Local`), and `editor_init` uses it to fill a window's `authority_spec` when the resolved one is `Local`. A CLI `ssh://` launch previously left the spec `Local`, which made workspace persistence and the manual-reconnect rebuild (`start_remote_reconnect`, which matches on the spec) silently inert for it. Never downgrades an already-remote spec. #2 Event-driven reconnect. `AgentChannel` gets a stable `id` and a `reconnect_notify` fired on each `replace_transport` hot-swap. New `FileSystem::remote_channel_id` / `remote_reconnect_notify` expose them; a lazily-spawned per-window forwarder turns a hot-swap into `AsyncMessage::RemoteReconnected { connection_id }`. This replaces the per-frame `is_remote_connected()` poll — it wakes the loop immediately, carries identity, and can't miss a fast flap. #3 One reconnect handler. `handle_remote_reconnected` maps the connection id back to its window and calls the new `reattach_window`, which clears the disconnect indicator and respawns the embedded terminals that died with the old carrier. The app-level `RemoteAttachMode::Reconnect` rebuild path now routes through the same `reattach_window`, so both the silent hot-swap and the manual/dormant rebuild converge on one idempotent path (respawn skips live terminals). Drops the old `remote_was_connected` field and the double-respawn guard. The e2e test now drives the reconnect dispatch (id→window→reattach), covering revive, wrong-id no-op, and duplicate-event idempotency. Note: true remote-terminal *session* survival across a reconnect (a server-side multiplexer so the shell and its processes outlive the carrier, rather than respawning a fresh shell) is left to a separate effort. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SHCFsZ3ReFxbqBgjwwBPdG
sinelaw
pushed a commit
that referenced
this pull request
Jun 24, 2026
Replaces the per-frame reconnect poller with an event-driven path and unifies the two reconnect mechanisms, and makes a plain `fresh ssh://…` launch a first-class remote session. Three connected changes: #1 Uniform backend spec. `Authority::session_spec()` derives the persistable `SessionAuthoritySpec` from the live authority's `command_wrap` (SSH/Kube → `RemoteAgent`, local/container → `Local`), and `editor_init` uses it to fill a window's `authority_spec` when the resolved one is `Local`. A CLI `ssh://` launch previously left the spec `Local`, which made workspace persistence and the manual-reconnect rebuild (`start_remote_reconnect`, which matches on the spec) silently inert for it. Never downgrades an already-remote spec. #2 Event-driven reconnect. `AgentChannel` gets a stable `id` and a `reconnect_notify` fired on each `replace_transport` hot-swap. New `FileSystem::remote_channel_id` / `remote_reconnect_notify` expose them; a lazily-spawned per-window forwarder turns a hot-swap into `AsyncMessage::RemoteReconnected { connection_id }`. This replaces the per-frame `is_remote_connected()` poll — it wakes the loop immediately, carries identity, and can't miss a fast flap. #3 One reconnect handler. `handle_remote_reconnected` maps the connection id back to its window and calls the new `reattach_window`, which clears the disconnect indicator and respawns the embedded terminals that died with the old carrier. The app-level `RemoteAttachMode::Reconnect` rebuild path now routes through the same `reattach_window`, so both the silent hot-swap and the manual/dormant rebuild converge on one idempotent path (respawn skips live terminals). Drops the old `remote_was_connected` field and the double-respawn guard. The e2e test now drives the reconnect dispatch (id→window→reattach), covering revive, wrong-id no-op, and duplicate-event idempotency. Note: true remote-terminal *session* survival across a reconnect (a server-side multiplexer so the shell and its processes outlive the carrier, rather than respawning a fresh shell) is left to a separate effort. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SHCFsZ3ReFxbqBgjwwBPdG
sinelaw
added a commit
that referenced
this pull request
Jul 1, 2026
…me history Addresses the remaining round-2 review findings. #3 — The Rust git-index BFS (app/git_index.rs) scanned one directory level deeper than the TypeScript discoverSubRepos it is documented to mirror, so a sub-repo exactly 4 levels below the workspace root got its index watched but was never decorated. Reframe the walk in terms of the level being scanned so both sides scan levels 1..=3, and spell out the shared contract in both docs. #6 — git_find_file listed every entry by its absolute path (long, shared prefix, worse fuzzy ranking) because label and open-path were the same string. Split into { rel, abs }: display/match the repo-relative path, open the absolute one. Also resolve the repo via resolveGitRepo so it works from a sub-project buffer when the workspace root isn't itself a repo. #7 — git_blame fetched historical content with `git show <rev>:<abs-path>`, which is fatal for an absolute path and fell through to the *current* working-tree content. Refer to the file as `<rev>:./<name>` from its own directory so git resolves it cwd-relative for any nesting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sinelaw
added a commit
that referenced
this pull request
Jul 7, 2026
…me history Addresses the remaining round-2 review findings. #3 — The Rust git-index BFS (app/git_index.rs) scanned one directory level deeper than the TypeScript discoverSubRepos it is documented to mirror, so a sub-repo exactly 4 levels below the workspace root got its index watched but was never decorated. Reframe the walk in terms of the level being scanned so both sides scan levels 1..=3, and spell out the shared contract in both docs. #6 — git_find_file listed every entry by its absolute path (long, shared prefix, worse fuzzy ranking) because label and open-path were the same string. Split into { rel, abs }: display/match the repo-relative path, open the absolute one. Also resolve the repo via resolveGitRepo so it works from a sub-project buffer when the workspace root isn't itself a repo. #7 — git_blame fetched historical content with `git show <rev>:<abs-path>`, which is fatal for an absolute path and fell through to the *current* working-tree content. Refer to the file as `<rev>:./<name>` from its own directory so git resolves it cwd-relative for any nesting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Updates the requirements on tree-sitter-highlight to permit the latest version.
Release notes
Sourced from tree-sitter-highlight's releases.
Commits
6e061870.25.264665ecDecrease the MSRV for the tree-sitter-language crate (#4221) (#4222)1925a70Reset result_symbol field of lexer in wasm memory in between invocations (#42...02625fcIgnore external tokens that are zero-length and extra (#4213) (#4216)d799b78Fix crash when loading languages w/ old ABI via wasm (#4210)f5afe47build: bump version to 0.25.1f20d4b0docs: correct build steps for WASM files05d443astyle(rust): correct doc commentseed662dfix(bindings): correct Zig bindings to expose alanguagefunction9ad096efix(lib): prevent finished_tree assertion failureDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot mergewill merge this PR after your CI passes on it@dependabot squash and mergewill squash and merge this PR after your CI passes on it@dependabot cancel mergewill cancel a previously requested merge and block automerging@dependabot reopenwill reopen this PR if it is closed@dependabot closewill close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)