Skip to content

boo ui: libghostty info(stream) logs (e.g. OSC 1 from Powerlevel10k) leak onto the rendered pane and corrupt it #64

@raphaelk-medsimples

Description

@raphaelk-medsimples

Summary

In boo ui, libghostty's VT stream parser logs unimplemented sequences at info level under the stream scope (e.g. info(stream): OSC 1 (change icon) received and ignored). The UI renders in-process in the user's terminal (it hosts its own stream parser in src/ui.zig and refuses to start without a TTY), with stderr inherited — so those log lines paint directly over the rendered viewport and corrupt it. C-a l (redraw) repaints boo's clean screen model and restores it; the underlying session is never actually affected, and boo peek stays clean (confirming this is stderr output, not the screen model).

Powerlevel10k is a common trigger: it emits an OSC 1 (set icon) on every prompt and on preexec, so the pane fills with ... received and ignored icon=~ / icon=<cmd> lines during normal use. Running e.g. cat ~/.zshrc reliably reproduces it.

Image

startDaemon (src/main.zig) redirects the daemon's stderr to BOO_LOG or /dev/null:

// Detach stdio. Keep stderr pointed at BOO_LOG if set so std.log output is preserved.
const devnull = posix.open("/dev/null", ...);
posix.dup2(devnull, 0); posix.dup2(devnull, 1);
if (posix.getenv("BOO_LOG")) |log_path| { ... posix.dup2(fd, 2); } else { posix.dup2(devnull, 2); }

boo ui runs via ui.run(...) without any such redirect, so the same info(stream) logs go straight to the user's terminal and over the rendered pane.

Environment

  • macOS (Darwin 25.5.0), arm64; iTerm2 3.6.11; zsh + oh-my-zsh + Powerlevel10k

Verified: this is not an optimization-level issue

I built v0.5.18 from source at both Debug and ReleaseSafe (the config release.yml ships) and drove a session that emits a single OSC 1, capturing BOO_LOG:

build info(stream) lines emitted
ReleaseSafe (release config) 1
Debug 1

So libghostty-vt emits these info logs regardless of optimize mode — building release does not fix it (releases already use ReleaseSafe). The fix has to filter the log.

Reproduce

Interactive (shows the corruption): with a Powerlevel10k prompt, boo uicat ~/.zshrc → the pane fills with info(stream) lines → C-a l clears it.

Headless (shows the root-cause log line), via Docker:

FROM debian:bookworm-slim
ARG BOO_ARCH=aarch64          # x86_64 on Intel hosts
ARG BOO_VERSION=v0.5.18
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl \
    && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL -o /tmp/boo.tar.gz \
      "https://github.com/coder/boo/releases/download/${BOO_VERSION}/boo-${BOO_ARCH}-linux.tar.gz" \
    && tar -xzf /tmp/boo.tar.gz -C /usr/local/bin boo && chmod +x /usr/local/bin/boo
CMD bash -c '\
  export BOO_DIR=/tmp/s BOO_LOG=/tmp/boo.log; : > "$BOO_LOG"; \
  boo new repro -d -- sh -c "printf \"\033]1;demo-icon\007\"; sleep 2"; sleep 3; \
  echo "--- peek (parsed screen, stays clean) ---"; boo peek repro; \
  echo "--- BOO_LOG (the leaked stream-scope line) ---"; cat "$BOO_LOG"; \
  boo kill --all'
docker build --platform linux/arm64 -t boo-repro . && docker run --rm --platform linux/arm64 boo-repro

Output — the parser logs the OSC even though peek is clean:

--- peek (parsed screen, stays clean) ---
--- BOO_LOG (the leaked stream-scope line) ---
info(stream): OSC 1 (change icon) received and ignored icon=demo-icon

(The container can't render the visual corruption — that needs an interactive boo ui on a real TTY, since a detached -d daemon sends stderr to /dev/null — but it deterministically reproduces the log line that leaks into the viewport.)

Suggested fix

boo installs no std_options, so logging follows Zig defaults. A logFn that drops info-and-below from the stream scope fixes it while leaving boo's own .ui/.daemon/.window logs intact. I've opened a draft PR with this change (verified zig fmt clean, zig build test passes, and the emitted info(stream) count drops 1 → 0 at ReleaseSafe).

I've created a draft PR that fixed the problem, but I still need to research if this is the best approach, since I'm not fully fluent in this stack

Workaround for users

oh-my-zsh DISABLE_AUTO_TITLE="true" (Powerlevel10k may also need its own title hooks disabled) removes the OSC-1 trigger — but any program emitting an unhandled sequence still corrupts boo ui until the log is filtered.


Thanks for building boo — the libghostty-backed "screen comes back exactly as a human would see it" model is genuinely great to use, and the peek/send/wait automation surface is lovely. Happy to adjust the fix to whatever shape you prefer.

Investigation note: I used an LLM (Claude) to help investigate and draft this report (If it's helpful, I can add the session history.) then verified every claim by building boo from source and testing — including disproving two of my own initial hypotheses (a #58 regression, and an optimization-level cause). This is a known class of bug — terminal-library log output reaching the TTY a TUI renders into; cf. opencode#6546.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions