Skip to content

Fix #5 (live view + playback): UTF-8 console output codepage management (Bsn.Host 0.2.3) - #18

Merged
soroshsabz merged 3 commits into
mainfrom
fix/live-view-codepage
Nov 7, 2026
Merged

soroshsabz merged 3 commits into
mainfrom
fix/live-view-codepage

Conversation

@soroshsabz

@soroshsabz soroshsabz commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

ITNOA

What you hit

After installing 0.2.2 from the gallery, OpenCode recordings rendered
box-drawing borders as CP437 soup in two places:

  1. The live view during recording - the whole TUI was mojibake
    (Gamma-ohmega-acute everywhere).
  2. After the live-view fix, Show-InteractiveTranscript still showed a
    column of blue ? where OpenCode draws its blue left border - while
    the logo, the text and the blocks rendered fine.

Root causes (two different layers, both fixed)

1. Live view (PtyRecorder): the ConPTY proxy writes the stream to
the real console as raw UTF-8 bytes, and the console decodes those
bytes with its own output codepage (OEM 437 in cmd / Windows PowerShell
5.1 hosts / fresh hidden consoles). The engine now saves the console
output codepage, switches it to UTF-8 (65001) for the duration of the
recording, and restores the original in finally.

2. Playback (Show-InteractiveTranscript): the recorded cast is
lossless UTF-8 (every capture test passed - the borders are U+2503
heavy vertical in the cast), but Windows PowerShell 5.1 (.NET Framework)
writes [Console] output through the console output codepage
(WriteFile), so characters that do not exist in CP437 (the heavy
border U+2503, corner U+2579, corners U+256D...) became literal
? while ASCII and CP437 blocks (U+2580/U+2584/U+2588 - the
logo) survived. pwsh 7 uses WriteConsoleW and was never affected -
which is why the pwsh-only tests could not catch it. The playback now
switches the console output encoding to UTF-8 for its duration and
restores it afterwards.

A char-level analysis of a real recorded OpenCode cast drove the
diagnosis: every character that rendered in the broken screenshot is in
CP437; every character that became ? is not.

What ships (Bsn.Host 0.2.3)

Change Layer
PtyRecorder: save console output codepage -> 65001 during recording -> restore in finally live view
Show-InteractiveTranscript: save [Console]::OutputEncoding -> UTF-8 during playback -> restore in finally (guarded; falls back gracefully on hosts that refuse) viewer
New system test: live view - conlog wraps a runner at OEM 437 while a byte-writing child records; capture must render glyphs byte-for-byte with zero CP437 mojibake and the codepage must be restored live view
New system test: playback - a real recording played back through powershell.exe 5.1 inside a conlog pseudoconsole at OEM 437; corners/borders/markers must render with zero rendered ?, codepage restored viewer
New unit tests: console output encoding restored after playback; heavy box characters survive a CP437-encoding console losslessly (child-process, discriminating) viewer
Console-logger cross-check test: add the redirected-stdio skip guard its asciinema/tmux siblings already had tests
Docs updated in all three artifacts (comment help, about topic, docs md); CHANGELOG for 0.2.3 docs

Verified locally

  • Built conlog from rhubarb-geek-nz/console-logger (as CI does).
  • Live view: pre-fix build -> new test RED (corner count 0, mojibake
    soup); fixed build -> GREEN.
  • Playback: unfixed cmdlet -> both new tests RED (┃ degraded to ?,
    corner count 0); fixed cmdlet -> GREEN.
  • Full gate: analyzer clean, 96 tests green (8 + 74 + 14), both conlog
    system tests running (not skipped).

Please verify on your machine

Install-Module Bsn.Host -RequiredVersion 0.2.3   # after this PR merges and v0.2.3 is released
Import-Module Bsn.Host
Start-InteractiveTranscript opencode             # live view
Show-InteractiveTranscript .\opencode.cast       # viewer

from the same console that showed the mojibake and the ? column.

Field regression from v0.2.2: the CAST is clean (every capture test
passes), but the LIVE view - what the user watches while recording -
still turns box-drawing text into CP437 mojibake on OEM-codepage
consoles (cmd, Windows PowerShell 5.1 hosts, fresh hidden consoles).
Reported with OpenCode after installing 0.2.2 from the gallery.

Root cause: PtyRecorder proxies the ConPTY stream to the real console
as raw UTF-8 bytes (WriteFile on the console handle). The console
decodes those bytes with its own OUTPUT codepage; at the OEM codepage
the UTF-8 box borders (E2 94 80 ...) render as 'Gamma-ohmega-acute'.
The recorder never re-encodes, so no cast-side fix can help: the outer
console is the decoder.

Fix: the engine saves the console output codepage, switches it to
UTF-8 (65001) for the duration of the recording - the proxied stream
is UTF-8, matching the cast - and restores the original value in
finally, alongside the existing console-input-mode restore. Consoles
already at 65001 (pwsh 7 hosts) are untouched.

Test: new Windows system test reproduces the exact topology end to
end. conlog wraps a runner whose console sits at the OEM codepage
while a byte-writing child records; the conlog capture IS the live
view the user would see. Verified locally against a pre-fix build
(test red: corner count 0, mojibake soup) and the fixed build (green:
glyphs byte-for-byte, console codepage restored to 437). Runs on CI
whenever the console-logger build step succeeds; skips gracefully
otherwise. Also adds the redirected-stdio skip guard the existing
console-logger cross-check was missing (conlog cannot run when its
own stdin is redirected).

Bsn.Host 0.2.3 (C# engine + docs + version stamp); Bsn.Common and
Bsn.SystemManagement are unchanged.
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 90e64b8a-c963-4626-bbf0-4aaa74899d1b
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@codecov

codecov Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.36364% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Bsn.Host/Public/Show-InteractiveTranscript.ps1 86.36% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

…w-InteractiveTranscript

Second half of the viewer bug: the recorded cast is lossless UTF-8, but
Show-InteractiveTranscript in Windows PowerShell 5.1 (.NET Framework)
writes [Console] output through the console OUTPUT CODEPAGE (WriteFile),
so heavy box-drawing characters that do not exist in CP437 - OpenCode's
U+2503 border and U+2579 corner - degraded to literal '?' in the viewer
while ASCII and CP437 blocks (the logo, the text) survived. pwsh 7 uses
WriteConsoleW and was never affected, which is why the pwsh-only tests
did not catch it.

Show-InteractiveTranscript now saves the console output encoding,
switches it to UTF-8 for the playback, and restores the original
afterwards (same save/switch/restore discipline the recording engine
uses for the live proxy view). Consoles already at UTF-8 are untouched;
hosts that refuse the switch fall back to the previous behavior via a
guarded setter.

Tests:
- Unit: playback restores the console output encoding it found.
- Unit (discriminating): a child pwsh with redirected stdout encodes
  [Console]::Write through [Console]::OutputEncoding - the same lossy
  path as an interactive 5.1 host. With a cast containing U+2503/U+2579
  and the console forced to CP437, the output must contain the UTF-8
  characters and no '?' (red on the unfixed cmdlet, green with the fix).
- System (end to end): a real recording played back through
  powershell.exe 5.1 inside a conlog pseudoconsole at the OEM codepage -
  corners, borders and markers must render with zero rendered '?', and
  the console codepage must be restored (red unfixed, green fixed).
- Also adds the redirected-stdio skip guard the console-logger
  cross-check test was missing (conlog cannot run when its own stdin is
  redirected).
@soroshsabz soroshsabz changed the title Fix #5 (live view): switch the console to UTF-8 while recording (Bsn.Host 0.2.3) Fix #5 (live view + playback): UTF-8 console output codepage management (Bsn.Host 0.2.3) Nov 7, 2026
@soroshsabz soroshsabz self-assigned this Nov 7, 2026
@soroshsabz soroshsabz added the bug Something isn't working label Nov 7, 2026
…tionally so the lossless-playback test runs on Linux/macOS CI (both failed with NotSupportedException there).
@sonarqubecloud

sonarqubecloud Bot commented Nov 7, 2026

Copy link
Copy Markdown

@soroshsabz
soroshsabz merged commit 6944d75 into main Nov 7, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cast recordings corrupt multi-byte UTF-8 characters at 4096-byte chunk boundaries (mojibake with oh-my-posh / opencode TUI / bulk output)

1 participant