Skip to content

Documented OSC 7 shell integration is silently ignored; plus image decode on the parse thread and per-session thread cost #344

Description

@kshivang

Found while porting the emulator to Rust for BossConsoleRust. Verified against the current checkout.

1. The documented shell-integration setup does nothing — OSC 7 is never dispatched

BossEmulator1.kt:403:

7 ->         // Support for OSC 7 is pending

The arm returns true, which suppresses the unhandled-sequence log, and then never dispatches anything. Bare OSC 7 is unimplemented.

Meanwhile .claude/rules/shell-integration.md instructs users to emit exactly that sequence:

:11  PROMPT_COMMAND='echo -ne "\033]7;file://${HOSTNAME}${PWD}\007"'
:16  precmd() { echo -ne "\033]7;file://${HOST}${PWD}\007" }

So a user who follows the documented setup gets silently no cwd tracking and no cwd-derived tab titles. Nothing warns them; the sequence is accepted and discarded. It works only for users running BossTerm's own shell-integration scripts, because those report cwd through the private OSC 1341;7;file://… sub-protocol, which is routed.

Two ways to close it, and the choice matters:

  • Route OSC 7 to the same handler 1341;7 reaches. This is the standard sequence (VTE, iTerm2, WezTerm, Kitty all honour it), so supporting it also means cwd tracking works for users arriving with an existing dotfile setup.
  • Or correct the docs to stop advertising a sequence the emulator drops.

The first is preferable — OSC 7 is the interoperable spelling, and the private sub-protocol then becomes an addition rather than the only path.

2. Inline images are fully decoded on the emulator's parse thread

processITerm2File calls ImageIO.read solely to obtain width and height. That decodes the entire image on the thread parsing PTY output, so a large inline image stalls parsing of everything queued behind it in the stream — visible as the terminal freezing mid-output.

Dimensions are available from the header alone (PNG IHDR, JPEG SOF, GIF logical screen descriptor, WebP VP8X), which is O(header) and yields identical numbers. Decode can then happen off-thread, or lazily at render time.

3. Three pinned Dispatchers.IO workers per shell session

Confirmed in source, and consistent with the known symptom: ~20 sessions exhausts the 64-thread Dispatchers.IO cap, at which point terminals freeze and new tabs open with no shell at all.

For reference, the Rust port needs 2 threads per session plus one process-wide reaper: child-exit waiting does not need a thread per child, since liveness comes from PTY EOF and only the exit code trails (bounded there at ≤200 ms). A tests/thread_budget.rs opens 6 real PTYs and asserts the exact per-session cost and full teardown, so the budget is measured rather than assumed — worth doing here too, since this class of bug is invisible until it is severe.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions