Random freezing #1

Closed
opened 2026-07-22 19:55:20 +08:00 by Breadway · 13 comments
Owner

When I search for something, sometimes the window freezes and the only way to fix it is to exit and open again

When I search for something, sometimes the window freezes and the only way to fix it is to exit and open again
Author
Owner

Investigated on 2026-08-06. Traced the full search path (main() → run_ui() → search.connect_changed() → fuzzy_matches()/fuzzy_score(), plus breadbox-shared's desktop-entry/icon-cache/config loading and breadbox-sync's manifest writer) and found no blocking I/O, locks, channels, or threads anywhere in the codebase — it's entirely single-threaded on the GTK main loop, and every function in the search/filter/sort path is bounded, in-memory, and non-blocking. breadbox-sync is a fully offline batch tool, not something breadbox talks to at runtime, and it writes manifest.json atomically (tmp file + rename), so there's no race there either.

No definitive root cause found, so no fix was made — didn't want to guess-patch something unrelated. Two ranked hypotheses, neither implemented:

  1. Wayland/gtk4-layer-shell keyboard-grab issue — window.set_keyboard_mode(KeyboardMode::Exclusive) is a known trouble spot for some compositors: the window can stop receiving keystrokes on reopen while the process itself is fine (matches "only fix is quit and reopen"). This would be a compositor interaction, not fixable purely in breadbox's code.
  2. Per-keystroke resort cost on large entry counts — every keystroke does a full re-sort with repeated to_lowercase() allocations, which could read as a stutter under load but wouldn't explain an indefinite freeze.

To narrow this down: when it freezes, does the window disappear/stop rendering entirely, or does it stay visible but just stop responding to keystrokes? That would help distinguish (1) from an actual process hang.

Investigated on 2026-08-06. Traced the full search path (`main()` → `run_ui()` → `search.connect_changed()` → `fuzzy_matches()`/`fuzzy_score()`, plus `breadbox-shared`'s desktop-entry/icon-cache/config loading and `breadbox-sync`'s manifest writer) and found no blocking I/O, locks, channels, or threads anywhere in the codebase — it's entirely single-threaded on the GTK main loop, and every function in the search/filter/sort path is bounded, in-memory, and non-blocking. `breadbox-sync` is a fully offline batch tool, not something breadbox talks to at runtime, and it writes `manifest.json` atomically (tmp file + rename), so there's no race there either. No definitive root cause found, so no fix was made — didn't want to guess-patch something unrelated. Two ranked hypotheses, neither implemented: 1. **Wayland/gtk4-layer-shell keyboard-grab issue** — `window.set_keyboard_mode(KeyboardMode::Exclusive)` is a known trouble spot for some compositors: the window can stop receiving keystrokes on reopen while the process itself is fine (matches "only fix is quit and reopen"). This would be a compositor interaction, not fixable purely in breadbox's code. 2. **Per-keystroke resort cost** on large entry counts — every keystroke does a full re-sort with repeated `to_lowercase()` allocations, which could read as a stutter under load but wouldn't explain an indefinite freeze. To narrow this down: when it freezes, does the window disappear/stop rendering entirely, or does it stay visible but just stop responding to keystrokes? That would help distinguish (1) from an actual process hang.
Author
Owner

User confirms: window stays visible, just stops responding to keystrokes when it freezes.

This is consistent with hypothesis 1 (keyboard-grab/compositor issue) but doesn't fully rule out a true main-loop hang, since a frozen GTK main loop would also just leave the last-rendered frame on screen (Wayland surfaces are client-painted, so "visible" alone doesn't prove the loop is still running).

Next time it happens, a couple of cheap checks would disambiguate:

  • Hover the mouse over the window/list items during the freeze. If hover-highlight still updates, the GTK main loop is alive and it's specifically input (keyboard) that's stuck — points hard at hypothesis 1. If hover does nothing either, the whole loop is blocked — a real hang, and worth revisiting the "no blocking calls found" conclusion (maybe under conditions not covered by static tracing, e.g. filesystem stalls on the desktop-entry directories).
  • ps -o pid,stat,%cpu -C breadbox during the freeze — S/D state with near-0% CPU means it's blocked waiting on something (I/O or a lock); a busy R state pegged at 100% means it's spinning, which would point at something like a runaway match/sort loop instead.
  • If launched from a terminal, note whether anything prints to stderr right as it freezes.

Leaving this here for whenever it's caught in the act again.

User confirms: window stays visible, just stops responding to keystrokes when it freezes. This is consistent with hypothesis 1 (keyboard-grab/compositor issue) but doesn't fully rule out a true main-loop hang, since a frozen GTK main loop would also just leave the last-rendered frame on screen (Wayland surfaces are client-painted, so "visible" alone doesn't prove the loop is still running). Next time it happens, a couple of cheap checks would disambiguate: - **Hover the mouse over the window/list items during the freeze.** If hover-highlight still updates, the GTK main loop is alive and it's specifically input (keyboard) that's stuck — points hard at hypothesis 1. If hover does nothing either, the whole loop is blocked — a real hang, and worth revisiting the "no blocking calls found" conclusion (maybe under conditions not covered by static tracing, e.g. filesystem stalls on the desktop-entry directories). - **`ps -o pid,stat,%cpu -C breadbox`** during the freeze — `S`/`D` state with near-0% CPU means it's blocked waiting on something (I/O or a lock); a busy `R` state pegged at 100% means it's spinning, which would point at something like a runaway match/sort loop instead. - If launched from a terminal, note whether anything prints to stderr right as it freezes. Leaving this here for whenever it's caught in the act again.
Author
Owner

This does not seem to be widely reproducible, suggest adding a way for users to share logs or rust traces easily for development help?

This does not seem to be widely reproducible, suggest adding a way for users to share logs or rust traces easily for development help?
Author
Owner

Structured File Logging (tracing + tracing-appender): Flush low-overhead logs to $XDG_STATE_HOME/breadbox/breadbox.log. Capturing GTK focus signals, key-press events, and search iteration timings under TRACE or DEBUG gives you an exact timeline leading up to the freeze.

Diagnostic Command (breadbox --bug-report): Implement a flag or command that bundles environment details (XDG_CURRENT_DESKTOP, WAYLAND_DISPLAY, GTK/layer-shell version, KeyboardMode), active config, and the last 200 lines of the log file into a clean markdown snippet ready to copy-paste.

Backtrace & Panic Hooks: Use color-backtrace or a custom panic hook to guarantee that any unexpected Rust thread panics or signal traps flush their stack traces directly to disk before the process terminates.

Focus & Frame Probes: To test Hypothesis 1 directly, log gtk4-layer-shell focus enter/leave signals alongside a periodic GTK GLib timeout tick (e.g., every 500ms). If the periodic tick log keeps printing while key events stop arriving, you have bulletproof evidence of a compositor focus drop rather than a GTK main loop hang.

Structured File Logging (tracing + tracing-appender): Flush low-overhead logs to $XDG_STATE_HOME/breadbox/breadbox.log. Capturing GTK focus signals, key-press events, and search iteration timings under TRACE or DEBUG gives you an exact timeline leading up to the freeze. Diagnostic Command (breadbox --bug-report): Implement a flag or command that bundles environment details (XDG_CURRENT_DESKTOP, WAYLAND_DISPLAY, GTK/layer-shell version, KeyboardMode), active config, and the last 200 lines of the log file into a clean markdown snippet ready to copy-paste. Backtrace & Panic Hooks: Use color-backtrace or a custom panic hook to guarantee that any unexpected Rust thread panics or signal traps flush their stack traces directly to disk before the process terminates. Focus & Frame Probes: To test Hypothesis 1 directly, log gtk4-layer-shell focus enter/leave signals alongside a periodic GTK GLib timeout tick (e.g., every 500ms). If the periodic tick log keeps printing while key events stop arriving, you have bulletproof evidence of a compositor focus drop rather than a GTK main loop hang.
Author
Owner

Re-investigated for BOS 0.6.2 release prep. Read the full search/render path (breadbox/src/main.rs, bread-launcher's ResultsList in bread-ecosystem, bread-utils' singleton/gtk_popup helpers) and did a bounded live-reproduction attempt on the real Hyprland session. Net: still no fix I can verify, but I can narrow the hypothesis and rule out several suspects.

Ruled out (read the code, all synchronous and cheap):

  • search.connect_changed -> ResultsList::set_query (bread-launcher/src/gtk.rs): plain in-memory iteration over rows, fuzzy_matches/fuzzy_score are simple subsequence/prefix checks, invalidate_sort re-runs an in-memory comparator. No I/O, no locking, no async work happens per keystroke. For the app counts breadbox deals with (dozens, not thousands) this cannot itself block the main loop for a user-noticeable duration.
  • Icon loading (make_icon, via glycin/gdk::Texture): happens once per row at ResultsList::new (start-up), not on every keystroke. Confirmed by tracing this myself: 27+ icons decode via glycin's sandboxed D-Bus loader in well under 200ms. Not on the typing path.
  • bread_utils::singleton::toggle_or_kill: uses an atomic flock, not the old read-then-kill TOCTOU pattern. Kill is SIGTERM (not -9), and the flock releases the instant the process dies regardless of clean shutdown, so there's no stale-lock deadlock case here. Solid design, not the cause.

Leading hypothesis, more specific than the prior pass: new_overlay_window (bread-utils/src/gtk_popup.rs) sets KeyboardMode::Exclusive on the layer-shell surface, i.e. breadbox asks Hyprland/gtk4-layer-shell to route all keyboard input to it exclusively while mapped. If that exclusive grab is ever dropped or not re-delivered by the compositor mid-session (a known rough edge in gtk4-layer-shell plus wlroots-family compositors), the result is exactly what's reported: the window stays mapped and rendered, GTK's own main loop is still alive and fine, but keystrokes just stop arriving at the search entry. That reads as "frozen" to the user, force-closing is the only recovery (a fresh process re-requests the grab), and it would not reproduce reliably since it's timing and compositor-state dependent, not a breadbox code bug per se. This isn't speculation out of nowhere: this exact library already caused a confirmed layer-surface behavior bug in this ecosystem, see memory note "bind-auto-unreliable-for-layer-surfaces": breadbar's OSD picked the wrong monitor via bind_auto/monitor_at_surface, fixed by pinning the surface explicitly. Same subsystem, same class of "the compositor doesn't do what gtk4-layer-shell asked" issue.

Live repro attempt: launched the real installed ~/.local/bin/breadbox under RUST_LOG=trace on this machine's actual Hyprland session and drove it. It did not reproduce a freeze. Instead the process built its rows (icon decode traffic visible in the log) and then exited entirely within about a second, without presenting a window in a screenshot and without any panic or error in the trace log. That's a different failure mode than what's reported (silent early exit vs. an unresponsive-but-mapped window), and launching from an agent-controlled shell doesn't have the same focus and input context a real keypress-triggered launch does, so I don't trust this as evidence of the same bug. Flagging it only as a data point, not a finding. If it turns out to reproduce for you too outside an agent shell, that would be a separate, more tractable bug (a crash, not a freeze) and worth its own issue.

Secondary, unrelated observation while reading main(): breadbox layers two independent single-instance mechanisms at once, its own flock-based toggle_or_kill, and GTK's default Application single-instance and D-Bus activation (only opted out of via NON_UNIQUE for --screenshot runs). I don't have evidence this causes the reported freeze, but it's a latent inconsistency (two different systems both deciding "am I the only instance") worth a closer look in its own pass. Not touching it here, out of scope for this ticket.

Recommended next step, not done here (deliberately out of scope for this ticket per its own "investigation only" framing): implement the diagnostic logging plan from the earlier comment on this issue, structured file logging to $XDG_STATE_HOME/breadbox/breadbox.log plus a periodic GLib timeout heartbeat alongside gtk4-layer-shell focus enter and leave probes. If the heartbeat keeps ticking in the log while key events stop, that's hard confirmation of the compositor-focus-drop hypothesis above rather than a genuine GTK-thread hang, and would finally make this reproducible enough to fix for real. Worth filing as its own small issue rather than folding into this one.

Leaving this in todo with the unresolvable label as-is: I don't have a fix I can verify, and I'm not willing to ship a speculative change against a compositor-timing bug I can't reproduce on demand. For 0.6.2 release notes: this is a known, intermittent, low-priority issue with a credible (but unconfirmed) root cause in the gtk4-layer-shell and Hyprland keyboard-exclusivity handshake, not in breadbox's own search or filtering logic, which is clean.

Re-investigated for BOS 0.6.2 release prep. Read the full search/render path (breadbox/src/main.rs, bread-launcher's ResultsList in bread-ecosystem, bread-utils' singleton/gtk_popup helpers) and did a bounded live-reproduction attempt on the real Hyprland session. Net: still no fix I can verify, but I can narrow the hypothesis and rule out several suspects. Ruled out (read the code, all synchronous and cheap): - `search.connect_changed` -> `ResultsList::set_query` (bread-launcher/src/gtk.rs): plain in-memory iteration over rows, `fuzzy_matches`/`fuzzy_score` are simple subsequence/prefix checks, `invalidate_sort` re-runs an in-memory comparator. No I/O, no locking, no async work happens per keystroke. For the app counts breadbox deals with (dozens, not thousands) this cannot itself block the main loop for a user-noticeable duration. - Icon loading (`make_icon`, via glycin/gdk::Texture): happens once per row at `ResultsList::new` (start-up), not on every keystroke. Confirmed by tracing this myself: 27+ icons decode via glycin's sandboxed D-Bus loader in well under 200ms. Not on the typing path. - `bread_utils::singleton::toggle_or_kill`: uses an atomic `flock`, not the old read-then-kill TOCTOU pattern. Kill is `SIGTERM` (not `-9`), and the flock releases the instant the process dies regardless of clean shutdown, so there's no stale-lock deadlock case here. Solid design, not the cause. Leading hypothesis, more specific than the prior pass: `new_overlay_window` (bread-utils/src/gtk_popup.rs) sets `KeyboardMode::Exclusive` on the layer-shell surface, i.e. breadbox asks Hyprland/gtk4-layer-shell to route all keyboard input to it exclusively while mapped. If that exclusive grab is ever dropped or not re-delivered by the compositor mid-session (a known rough edge in gtk4-layer-shell plus wlroots-family compositors), the result is exactly what's reported: the window stays mapped and rendered, GTK's own main loop is still alive and fine, but keystrokes just stop arriving at the search entry. That reads as "frozen" to the user, force-closing is the only recovery (a fresh process re-requests the grab), and it would not reproduce reliably since it's timing and compositor-state dependent, not a breadbox code bug per se. This isn't speculation out of nowhere: this exact library already caused a confirmed layer-surface behavior bug in this ecosystem, see memory note "bind-auto-unreliable-for-layer-surfaces": breadbar's OSD picked the wrong monitor via `bind_auto`/`monitor_at_surface`, fixed by pinning the surface explicitly. Same subsystem, same class of "the compositor doesn't do what gtk4-layer-shell asked" issue. Live repro attempt: launched the real installed `~/.local/bin/breadbox` under `RUST_LOG=trace` on this machine's actual Hyprland session and drove it. It did not reproduce a freeze. Instead the process built its rows (icon decode traffic visible in the log) and then exited entirely within about a second, without presenting a window in a screenshot and without any panic or error in the trace log. That's a different failure mode than what's reported (silent early exit vs. an unresponsive-but-mapped window), and launching from an agent-controlled shell doesn't have the same focus and input context a real keypress-triggered launch does, so I don't trust this as evidence of the same bug. Flagging it only as a data point, not a finding. If it turns out to reproduce for you too outside an agent shell, that would be a separate, more tractable bug (a crash, not a freeze) and worth its own issue. Secondary, unrelated observation while reading main(): breadbox layers two independent single-instance mechanisms at once, its own flock-based `toggle_or_kill`, and GTK's default `Application` single-instance and D-Bus activation (only opted out of via `NON_UNIQUE` for `--screenshot` runs). I don't have evidence this causes the reported freeze, but it's a latent inconsistency (two different systems both deciding "am I the only instance") worth a closer look in its own pass. Not touching it here, out of scope for this ticket. Recommended next step, not done here (deliberately out of scope for this ticket per its own "investigation only" framing): implement the diagnostic logging plan from the earlier comment on this issue, structured file logging to `$XDG_STATE_HOME/breadbox/breadbox.log` plus a periodic GLib timeout heartbeat alongside gtk4-layer-shell focus enter and leave probes. If the heartbeat keeps ticking in the log while key events stop, that's hard confirmation of the compositor-focus-drop hypothesis above rather than a genuine GTK-thread hang, and would finally make this reproducible enough to fix for real. Worth filing as its own small issue rather than folding into this one. Leaving this in `todo` with the `unresolvable` label as-is: I don't have a fix I can verify, and I'm not willing to ship a speculative change against a compositor-timing bug I can't reproduce on demand. For 0.6.2 release notes: this is a known, intermittent, low-priority issue with a credible (but unconfirmed) root cause in the gtk4-layer-shell and Hyprland keyboard-exclusivity handshake, not in breadbox's own search or filtering logic, which is clean.
Author
Owner

I followed the prior investigation direction. The search path and icon loading do not explain a per-keystroke freeze, and I could not reproduce the reported failure in the live Hyprland session. The leading hypothesis remains a dropped gtk4-layer-shell exclusive keyboard grab, but it is unconfirmed. I am leaving this in todo without a speculative fix. The next useful work is a separate diagnostic issue for persistent tracing plus a GLib heartbeat and layer-shell focus probes, so a real freeze can distinguish a main-loop stall from lost keyboard delivery.

I followed the prior investigation direction. The search path and icon loading do not explain a per-keystroke freeze, and I could not reproduce the reported failure in the live Hyprland session. The leading hypothesis remains a dropped gtk4-layer-shell exclusive keyboard grab, but it is unconfirmed. I am leaving this in todo without a speculative fix. The next useful work is a separate diagnostic issue for persistent tracing plus a GLib heartbeat and layer-shell focus probes, so a real freeze can distinguish a main-loop stall from lost keyboard delivery.
Author
Owner

BOX-8 investigation update (fresh clean origin/main clone): the search entry's connect_changed callback calls ResultsList::set_query and updates the footer count. The pinned bread-launcher implementation scans the already-loaded rows in memory, applies fuzzy matches, invalidates the list sort, then selects the first visible row. The popup requests KeyboardMode::Exclusive. This keeps a compositor focus or keyboard-grab loss plausible, but there is no evidence that it is the cause.

I built breadbox and captured its launcher under bread-capture's throwaway headless Sway; the window rendered normally. That capture path does not send typed input, so it cannot reproduce the reported input freeze. The only installed synthetic keyboard injector uses host uinput, which is unsuitable for driving this isolated session. I did not reproduce the freeze and found no verified code fix.

Recommended next step: a separate diagnostics task that writes persistent tracing under $XDG_STATE_HOME/breadbox, recording key events, focus enter/leave, per-query timing, and a 500 ms GLib heartbeat. If the heartbeat continues while key events stop, that distinguishes a lost compositor keyboard grab from a stalled GTK loop. Leave this issue in todo until a user reproduction produces evidence to fix.

BOX-8 investigation update (fresh clean origin/main clone): the search entry's `connect_changed` callback calls `ResultsList::set_query` and updates the footer count. The pinned bread-launcher implementation scans the already-loaded rows in memory, applies fuzzy matches, invalidates the list sort, then selects the first visible row. The popup requests `KeyboardMode::Exclusive`. This keeps a compositor focus or keyboard-grab loss plausible, but there is no evidence that it is the cause. I built breadbox and captured its launcher under bread-capture's throwaway headless Sway; the window rendered normally. That capture path does not send typed input, so it cannot reproduce the reported input freeze. The only installed synthetic keyboard injector uses host uinput, which is unsuitable for driving this isolated session. I did not reproduce the freeze and found no verified code fix. Recommended next step: a separate diagnostics task that writes persistent tracing under `$XDG_STATE_HOME/breadbox`, recording key events, focus enter/leave, per-query timing, and a 500 ms GLib heartbeat. If the heartbeat continues while key events stop, that distinguishes a lost compositor keyboard grab from a stalled GTK loop. Leave this issue in todo until a user reproduction produces evidence to fix.
Author
Owner

I split the requested telemetry into BOX-17: persistent input/focus/query timing traces and a GLib heartbeat. BOX-8 remains in todo until a real reproduction provides evidence for a functional fix.

I split the requested telemetry into BOX-17: persistent input/focus/query timing traces and a GLib heartbeat. BOX-8 remains in todo until a real reproduction provides evidence for a functional fix.
Author
Owner

Following the current investigation direction, there is still no verified functional fix to implement. BOX-17 has now been completed and merged as PR #20, adding persistent input, focus, query timing, and main-loop heartbeat diagnostics under $XDG_STATE_HOME/breadbox/. The next useful evidence is a user reproduction with the diagnostic report from that build, which can distinguish lost key delivery from a stalled main loop. I am leaving BOX-8 in TODO and not opening a speculative PR.

Following the current investigation direction, there is still no verified functional fix to implement. BOX-17 has now been completed and merged as PR #20, adding persistent input, focus, query timing, and main-loop heartbeat diagnostics under `$XDG_STATE_HOME/breadbox/`. The next useful evidence is a user reproduction with the diagnostic report from that build, which can distinguish lost key delivery from a stalled main loop. I am leaving BOX-8 in TODO and not opening a speculative PR.
Author
Owner

Reproduced a concrete search failure in an isolated headless Sway session with typed Wayland input. The fatal-critical stack traces through GLib WeakRef::clone, bread-theme::gtk::hook_subtree, a ListBox child-model reorder, and ResultsList::set_query. Sorting existing rows registered duplicate observers until GLib reported 'Too many GWeakRef registered'. Child-model reads during intermediate sorting also exposed invalid GObject references.

The verified shared fix is ready in bread-ecosystem PR 52: Breadway/bread-ecosystem#52 at 8a3df5f9f367f3e58e940ab014501720bb8e1c39. It deduplicates observers, breaks callback ownership cycles, disconnects released handlers, and coalesces updates after sorting completes.

Full workspace default/gtk/extract clippy and tests, build, fmt and pin lint pass. The isolated GTK regression covers 200 sorts of 160 rows plus reparenting and lifecycle. The launcher then completed 50 type/erase/arrow cycles with fatal criticals enabled: 299 query updates, no GLib warnings, still running. Median query duration fell from about 84 ms to 1.67 ms. Logs and harnesses are retained under /tmp/box8-probe-* and /tmp/box8-*.py for independent review.

This fixes the demonstrated failure path without claiming every intermittent Hyprland symptom has been reproduced. No installed binary, real desktop config or release tag was changed. The shared dependency still needs the normal reviewed integration and release.

Reproduced a concrete search failure in an isolated headless Sway session with typed Wayland input. The fatal-critical stack traces through GLib WeakRef::clone, bread-theme::gtk::hook_subtree, a ListBox child-model reorder, and ResultsList::set_query. Sorting existing rows registered duplicate observers until GLib reported 'Too many GWeakRef registered'. Child-model reads during intermediate sorting also exposed invalid GObject references. The verified shared fix is ready in bread-ecosystem PR 52: https://git.breadway.dev/Breadway/bread-ecosystem/pulls/52 at 8a3df5f9f367f3e58e940ab014501720bb8e1c39. It deduplicates observers, breaks callback ownership cycles, disconnects released handlers, and coalesces updates after sorting completes. Full workspace default/gtk/extract clippy and tests, build, fmt and pin lint pass. The isolated GTK regression covers 200 sorts of 160 rows plus reparenting and lifecycle. The launcher then completed 50 type/erase/arrow cycles with fatal criticals enabled: 299 query updates, no GLib warnings, still running. Median query duration fell from about 84 ms to 1.67 ms. Logs and harnesses are retained under /tmp/box8-probe-* and /tmp/box8-*.py for independent review. This fixes the demonstrated failure path without claiming every intermittent Hyprland symptom has been reproduced. No installed binary, real desktop config or release tag was changed. The shared dependency still needs the normal reviewed integration and release.
Author
Owner

Independent review of shared fix PR #52 at 8a3df5f9f367f3e58e940ab014501720bb8e1c39

Acceptance interpretation: prevent the observed search-triggered weak-reference exhaustion and keep the launcher responsive across repeated queries while retaining correct dynamic theme binding. bread-theme/src/gtk.rs:437-488 registers each child observer once, uses a weak registry capture, disconnects released handlers and defers tree traversal until the sort settles. The tests at gtk.rs:874-944 exercise 200 inverse sorts of 160 rows, reparenting and observer release. The reported isolated Sway input probe is the stronger behavioral check: 50 type/erase/arrow cycles, 299 query updates and no fatal GLib critical. CI is green and the PR is mergeable. This is a concrete fix for the reproduced failure path; it does not prove every intermittent Hyprland symptom is gone. Breadbox needs the reviewed shared dependency integrated/released before BOX-8 can be marked done. No merge attempted at the parent coordinator's checkpoint.

## Independent review of shared fix PR #52 at `8a3df5f9f367f3e58e940ab014501720bb8e1c39` Acceptance interpretation: prevent the observed search-triggered weak-reference exhaustion and keep the launcher responsive across repeated queries while retaining correct dynamic theme binding. `bread-theme/src/gtk.rs:437-488` registers each child observer once, uses a weak registry capture, disconnects released handlers and defers tree traversal until the sort settles. The tests at `gtk.rs:874-944` exercise 200 inverse sorts of 160 rows, reparenting and observer release. The reported isolated Sway input probe is the stronger behavioral check: 50 type/erase/arrow cycles, 299 query updates and no fatal GLib critical. CI is green and the PR is mergeable. This is a concrete fix for the reproduced failure path; it does not prove every intermittent Hyprland symptom is gone. Breadbox needs the reviewed shared dependency integrated/released before BOX-8 can be marked done. No merge attempted at the parent coordinator's checkpoint.
Author
Owner

Status 2026-10-02: the fix is on bread-ecosystem main but is still unreleased

Reviewed shared fix PR #52 and its release state.

  • ECO-52 is merged: f602406 "Merge pull request 'Bound GTK subtree observers during launcher searches' (#52) from fix/box-8-subtree-watch-growth into main". The fix commit is 8a3df5f ("fix: bound GTK subtree observers during launcher searches"), 146 insertions and 25 deletions in bread-theme/src/gtk.rs.
  • 8a3df5f is contained in no tag. The newest bread-ecosystem tags are v0.7.12, v0.8.0 and v0.8.1-rc.1, and the commit is an ancestor of none of them.
  • breadbox consumes bread-theme by tag: breadbox/Cargo.toml:12 pins tag = "v0.7.9" (with bread-utils, bread-screenshots and bread-launcher at v0.7.5). v0.7.9 predates the fix, so breadbox currently has no released dependency path to it.
  • BOX-17 / breadbox PR #20 (6924538, "Add persistent input and main-loop diagnostics") is merged into breadbox main, so the telemetry needed to confirm a real-world freeze is already in the shipped launcher.

Disposition

Keeping this in REVIEW. The unresolvable label now means "blocked on a shared release" rather than "not reproducible": the reproduced failure path (weak-reference exhaustion during search-triggered reorders) is fixed and merged in the shared crate, but breadbox cannot pick it up until a bread-ecosystem tag containing 8a3df5f is released and breadbox bumps its bread-theme pin onto it.

This card does not cascade from any merge, since it is a plain issue rather than a Closes #N PR. It can close once the release and the breadbox pin bump land. I am not cutting a release tag as part of a review pass.

Honest scope note: the fix is verified against the reproduced observer-growth failure path, not against every intermittent Hyprland keyboard symptom reported in earlier comments.

## Status 2026-10-02: the fix is on bread-ecosystem main but is still unreleased Reviewed shared fix PR #52 and its release state. - ECO-52 is merged: `f602406` "Merge pull request 'Bound GTK subtree observers during launcher searches' (#52) from fix/box-8-subtree-watch-growth into main". The fix commit is `8a3df5f` ("fix: bound GTK subtree observers during launcher searches"), 146 insertions and 25 deletions in `bread-theme/src/gtk.rs`. - `8a3df5f` is contained in **no tag**. The newest bread-ecosystem tags are `v0.7.12`, `v0.8.0` and `v0.8.1-rc.1`, and the commit is an ancestor of none of them. - breadbox consumes bread-theme by tag: `breadbox/Cargo.toml:12` pins `tag = "v0.7.9"` (with bread-utils, bread-screenshots and bread-launcher at `v0.7.5`). `v0.7.9` predates the fix, so breadbox currently has **no released dependency path** to it. - BOX-17 / breadbox PR #20 (`6924538`, "Add persistent input and main-loop diagnostics") is merged into breadbox main, so the telemetry needed to confirm a real-world freeze is already in the shipped launcher. ### Disposition Keeping this in REVIEW. The `unresolvable` label now means "blocked on a shared release" rather than "not reproducible": the reproduced failure path (weak-reference exhaustion during search-triggered reorders) is fixed and merged in the shared crate, but breadbox cannot pick it up until a bread-ecosystem tag containing `8a3df5f` is released and breadbox bumps its `bread-theme` pin onto it. This card does not cascade from any merge, since it is a plain issue rather than a `Closes #N` PR. It can close once the release and the breadbox pin bump land. I am not cutting a release tag as part of a review pass. Honest scope note: the fix is verified against the reproduced observer-growth failure path, not against every intermittent Hyprland keyboard symptom reported in earlier comments.
Author
Owner

Addendum: the 0.8.1 release candidate predates this fix

Since the comment above, a release candidate exists: bread-ecosystem v0.8.1-rc.1 at cb0af34. It does not clear the blocker.

  • 8a3df5f (the ECO-52 fix commit) is dated 2026-09-30 23:58.
  • cb0af34 and the v0.8.1-rc.1 tag are dated 2026-10-01 18:43.
  • The ECO-52 merge into main (f602406) landed 2026-10-02 12:06, after the tag was cut.

So the candidate was tagged from a main state that did not yet contain the merge. origin/main is now 16 commits ahead of v0.8.1-rc.1, and the missing commits include ECO-52 (8a3df5f), ECO-55 (656123e), ECO-61 (b54c4c8), ECO-62 (a4f8a52), ECO-63/64 (f9ea599) and ECO-49/50/57.

git merge-base --is-ancestor 8a3df5f v0.8.1-rc.1 is false. breadbox still has no released dependency path.

Related and worth tracking separately: BAR-48 was driving that release candidate to unblock breadbar's pinned builds, but breadbar origin/main still pins all four bread-ecosystem crates to v0.7.11, so its locked tag build is still unverified. That is outside the review queue (BAR-48 sits in DOING), so I have not touched it.

### Addendum: the 0.8.1 release candidate predates this fix Since the comment above, a release candidate exists: bread-ecosystem `v0.8.1-rc.1` at `cb0af34`. It does **not** clear the blocker. - `8a3df5f` (the ECO-52 fix commit) is dated 2026-09-30 23:58. - `cb0af34` and the `v0.8.1-rc.1` tag are dated 2026-10-01 18:43. - The ECO-52 **merge into main** (`f602406`) landed 2026-10-02 12:06, after the tag was cut. So the candidate was tagged from a main state that did not yet contain the merge. `origin/main` is now 16 commits ahead of `v0.8.1-rc.1`, and the missing commits include ECO-52 (`8a3df5f`), ECO-55 (`656123e`), ECO-61 (`b54c4c8`), ECO-62 (`a4f8a52`), ECO-63/64 (`f9ea599`) and ECO-49/50/57. `git merge-base --is-ancestor 8a3df5f v0.8.1-rc.1` is false. breadbox still has no released dependency path. Related and worth tracking separately: BAR-48 was driving that release candidate to unblock breadbar's pinned builds, but breadbar `origin/main` still pins all four bread-ecosystem crates to `v0.7.11`, so its locked tag build is still unverified. That is outside the review queue (BAR-48 sits in DOING), so I have not touched it.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Breadway/breadbox#1
No description provided.