---
title: "Mysterious Brokenness Hides Three Independent Issues"
canonical: https://dxdev.com/blog/2026-09-07_agent-dock-three-issues-one-mystery/
datePublished: 2026-08-29
---
The alert log at 12:49 PM said it plainly: three separate things sat behind the agent dock feeling broken and mysterious. That framing only exists because the debugging didn't start that way. It started with a dock that froze my desktop for seconds, vanished from alt-tab, and randomly disappeared behind other windows, and every fix I tried for one of those symptoms left the other two intact, so the thing kept feeling haunted.

## Symptom one: the freeze

The dock is a Tk panel that redraws its lane buttons whenever a window opens or closes anywhere on the desktop. My first theory was that redrawing was slow because Tk widget creation is slow. It isn't, particularly. The actual freeze was the panel tearing down and rebuilding every widget in the layout on every single desktop event, including events that changed nothing about which lanes existed. Open a Chrome tab, dock rebuilds. Close a terminal, dock rebuilds. On a busy desktop that's a rebuild every few seconds, each one blocking the Tk main loop long enough to read as a hang.

The fix wasn't a debounce. It was tracking a signature of the panel state and skipping the redraw when nothing had changed:

```python
self._sig = None  # force a redraw
```

is the escape hatch I use when a redraw genuinely is needed (spawning a new session), but the default path now diffs the list against the last-known signature and no-ops if it matches. Full rebuild became the exception, not the loop body.

## Symptom two: no taskbar handle

Separately, the dock had no way to be found or minimized because it was never registered as a real application window. It carried the `WS_EX_TOOLWINDOW` extended style, which Explorer's taskbar filters out by design. Diagnosing this took a wrong turn: my first fix attempt just called `ShowWindow` with `SW_SHOW` after flipping the style bit, on the theory that showing the window again would force Explorer to re-evaluate it. It didn't. The taskbar caches its button list at show/hide transitions, not on style changes, and `SW_SHOW` on an already-visible window is a no-op, so nothing happened. I confirmed it with UI Automation against `Shell_TrayWnd` directly:

```
taskbar buttons (0):
```

Zero, even after the "fix." The style bit was flipped correctly but Explorer never saw it. The actual fix was to force the hide/show transition explicitly:

```python
ex = u.GetWindowLongW(hw, GWL_EXSTYLE)
new = (ex & ~WS_EX_TOOLWINDOW) | WS_EX_APPWINDOW
u.SetWindowLongW(hw, GWL_EXSTYLE, new)
u.ShowWindow(hw, SW_HIDE)
time.sleep(0.15)
u.ShowWindow(hw, SW_SHOWNA)
```

`SW_SHOWNA`, not `SW_SHOW`, so the window reappears without stealing focus from whatever I'm typing into elsewhere. That's what turned the dock into an actual taskbar-visible application instead of a borderless popup nobody could grab.

Getting a taskbar button back introduced a new failure mode though: now the dock could be minimized, and the layout code placed windows by calling `.geometry()` on the Tk root whenever a lane's status changed. A geometry call on a minimized Tk window silently un-minimizes it. So a lane going idle three seconds after being put away would yank the panel back onto the screen, which is worse than not having a taskbar button at all: the tool now visibly argues with a decision I'd just made. The fix was a guard that reads the real Win32 minimized state (Tk itself doesn't know, since `iconify()` refuses on an override-redirect window) and skips placement entirely while minimized:

```python
def minimized(self) -> bool:
    h = user32.GetAncestor(wintypes.HWND(self.root.winfo_id()), GA_ROOT)
    return bool(h and user32.IsIconic(wintypes.HWND(h)))
```

with the layout function returning early when it's true, and the next real state change re-applying placement once it comes back.

## Symptom three: it goes behind things

The third thing was the panel losing its topmost pin under other always-on-top windows, a separate re-assertion bug the log lists as fixed alongside the other two, not something either of the fixes above touched.

## The part underneath all three

None of these three bugs shared a root cause. They shared a symptom description: "the dock feels broken in a way I can't pin down." That phrase is a tell that I'm looking at more than one bug, because a single well-defined bug usually gets described by its actual behavior, not by a mood.

What made the three easy to conflate is that none of the components involved had written boundaries. The dock, the desk layout engine, the session cockpit, and the shared vault all read and write overlapping window state, and we hadn't written down which one owns what. So the same afternoon we fixed the three dock bugs, we wrote the boundaries down too: where the vault, the desk layout engine, the cockpit, and the dock each begin. That document didn't fix any code. It answered the question that had been sitting underneath the whole debugging session, which was never "why is the dock buggy" but "which of these four systems is even responsible for this behavior." Once we'd answered that, each of the three symptoms had exactly one place to live, and stopped looking like a single ghost.
