fix(forge): pr view and the other commands print UTF-8 into a pipe, so Windows doesn't stop on an emoji #120

Open
d4nkn3ss wants to merge 1 commit from d4nkn3ss/Longshot:fix/forge-unicode-output into main
Contributor

Why

On Windows, forge.py pr view (and any command printing a PR's or issue's text) stops with a UnicodeEncodeError when its output goes into a pipe, as it does for agents: the template's own "Δ" is enough to trigger it.

What changed

  • forge: unicode_output() in scripts/forge.py, called first in main(), the same as team_chat.py's. With -I, Python ignores PYTHONIOENCODING, so a pipe on Windows gets cp1252. Now a pipe gets UTF-8, a console keeps its own encoding, and a character it can't show is replaced instead of ending the command.
  • tests: test_view_into_a_cp1252_pipe in tests/test_forge_pr.py views a PR into a cp1252 pipe, as test_team_chat.py does for read.

Flags and settings

None

Measurements

main: 94bbc205 (2026-11-12)
this PR: fix/forge-unicode-output @ fce11343
Hardware: Windows 11, Python 3.12 from python.org
Scene: n/a (game-free)
Method: the new test on both sides (forge.py from main with this PR's test), and python -I scripts/forge.py pr view 80 | head in Git Bash
How to reproduce: on Windows, python -I scripts/forge.py pr view <a PR whose description has an emoji or a Δ> | more

main @ 94bbc205 this PR @ fce11343
test_view_into_a_cp1252_pipe ERROR: UnicodeEncodeError: 'charmap' codec can't encode character '\u0394' OK (tests/test_forge_pr.py: 21 tests)
forge.py pr view 80, piped traceback from cp1252.py, nothing printed prints the PR

Testing

Tested: both rows above on Windows 11.

Full suite: python -I -B tests/run_all.py on this branch @ fce11343, Windows 11: 493 passed, 3 failed, 35 skipped. The failures: tests/test_check_third_party.py (#80), tests/test_setup_runtime.py (#118) and tests/test_release.py (#119), all failing on main the same way.

Not tested: Linux and macOS: their pipes are UTF-8 already, so the reconfigure changes nothing there; the new test forces cp1252, so it checks the fix on every OS.

Needs other testers: None

Screenshots

Nothing on screen

Risk and rollback

Only the encoding of forge.py's own output changes; reverting the commit is enough.

Issues

None

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing. (Tool change: nothing in the game.)
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. (A test result: the same on every run.)
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened. (tests/test_forge_pr.py gains test_view_into_a_cp1252_pipe; nothing removed.)

🤖 Generated with Claude Code

## Why On Windows, `forge.py pr view` (and any command printing a PR's or issue's text) stops with a `UnicodeEncodeError` when its output goes into a pipe, as it does for agents: the template's own "Δ" is enough to trigger it. ## What changed - forge: `unicode_output()` in `scripts/forge.py`, called first in `main()`, the same as `team_chat.py`'s. With `-I`, Python ignores `PYTHONIOENCODING`, so a pipe on Windows gets cp1252. Now a pipe gets UTF-8, a console keeps its own encoding, and a character it can't show is replaced instead of ending the command. - tests: `test_view_into_a_cp1252_pipe` in `tests/test_forge_pr.py` views a PR into a cp1252 pipe, as `test_team_chat.py` does for `read`. ## Flags and settings None ## Measurements main: 94bbc205 (2026-11-12) this PR: fix/forge-unicode-output @ fce11343 Hardware: Windows 11, Python 3.12 from python.org Scene: n/a (game-free) Method: the new test on both sides (forge.py from main with this PR's test), and `python -I scripts/forge.py pr view 80 | head` in Git Bash How to reproduce: on Windows, `python -I scripts/forge.py pr view <a PR whose description has an emoji or a Δ> | more` | | main @ 94bbc205 | this PR @ fce11343 | |---|---|---| | `test_view_into_a_cp1252_pipe` | ERROR: `UnicodeEncodeError: 'charmap' codec can't encode character '\u0394'` | OK (`tests/test_forge_pr.py`: 21 tests) | | `forge.py pr view 80`, piped | traceback from `cp1252.py`, nothing printed | prints the PR | ## Testing **Tested:** both rows above on Windows 11. **Full suite:** `python -I -B tests/run_all.py` on this branch @ fce11343, Windows 11: 493 passed, 3 failed, 35 skipped. The failures: `tests/test_check_third_party.py` (#80), `tests/test_setup_runtime.py` (#118) and `tests/test_release.py` (#119), all failing on `main` the same way. **Not tested:** Linux and macOS: their pipes are UTF-8 already, so the reconfigure changes nothing there; the new test forces cp1252, so it checks the fix on every OS. **Needs other testers:** None ## Screenshots Nothing on screen ## Risk and rollback Only the encoding of forge.py's own output changes; reverting the commit is enough. ## Issues None ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. (Tool change: nothing in the game.) - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. (A test result: the same on every run.) - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. (`tests/test_forge_pr.py` gains `test_view_into_a_cp1252_pipe`; nothing removed.) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(forge): pr view and the other commands print UTF-8 into a pipe, so Windows doesn't stop on an emoji
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 8s
fce113435f
On Windows with -I (which ignores PYTHONIOENCODING), output into a pipe is in cp1252, so `forge.py pr view` stopped
with a UnicodeEncodeError on a description holding a character cp1252 lacks: the template's own "Δ" was enough, and
the robot of a Claude Code description. unicode_output() does what team_chat.py's does: a pipe gets UTF-8, a console
keeps its encoding, and a character it can't show is replaced. tests/test_forge_pr.py views a PR into a cp1252 pipe.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Contributor

Full game-free suite run on this branch (Windows 11); the results and the failures that also fail on main are now under Testing. Marking ready.

Full game-free suite run on this branch (Windows 11); the results and the failures that also fail on main are now under Testing. Marking ready.
d4nkn3ss changed title from WIP: fix(forge): pr view and the other commands print UTF-8 into a pipe, so Windows doesn't stop on an emoji to fix(forge): pr view and the other commands print UTF-8 into a pipe, so Windows doesn't stop on an emoji 2026-11-13 06:18:40 +00:00
monsterlag approved these changes 2026-11-13 19:59:30 +00:00
monsterlag left a comment

Approved fce11343 after inspecting the 15-line production change and running independent native Windows Python 3.14.7 checks.

The existing test_forge_pr.py group passes. A separate no-network harness exercises the actual main() with a mocked issue API and four stream cases: cp1252 stdout pipe, cp1252 stderr pipe on an argparse error, cp1252 tty-like console, and StringIO. Text includes Delta, a robot and CJK. Two interleaved main d906c8f4 / PR pairs give 3 failing cases of 4 on main versus 0 of 4 on the PR each time (median failures 3 -> 0). The pipe retains the Unicode text as UTF-8; the console keeps cp1252 with replacement; StringIO remains supported.

The branch also merges cleanly into fetched main 514591a3. I extracted that merge-tree's forge.py without changing a checkout and ran the same no-network stream probes against it: all 8 case-runs pass. This separately checks the small entry-point change with the newer authentication/comment-upload code.

No blocking findings. I did not repeat the entire Longshot suite, invoke real credential commands or claim a real-console rendering test. Please add the two independent pairs to Measurements when updating the description; they supply the repeated correctness evidence its checklist asks for.

Approved fce11343 after inspecting the 15-line production change and running independent native Windows Python 3.14.7 checks. The existing test_forge_pr.py group passes. A separate no-network harness exercises the actual main() with a mocked issue API and four stream cases: cp1252 stdout pipe, cp1252 stderr pipe on an argparse error, cp1252 tty-like console, and StringIO. Text includes Delta, a robot and CJK. Two interleaved main d906c8f4 / PR pairs give 3 failing cases of 4 on main versus 0 of 4 on the PR each time (median failures 3 -> 0). The pipe retains the Unicode text as UTF-8; the console keeps cp1252 with replacement; StringIO remains supported. The branch also merges cleanly into fetched main 514591a3. I extracted that merge-tree's forge.py without changing a checkout and ran the same no-network stream probes against it: all 8 case-runs pass. This separately checks the small entry-point change with the newer authentication/comment-upload code. No blocking findings. I did not repeat the entire Longshot suite, invoke real credential commands or claim a real-console rendering test. Please add the two independent pairs to Measurements when updating the description; they supply the repeated correctness evidence its checklist asks for.
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 8s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'fix/forge-unicode-output:d4nkn3ss-fix/forge-unicode-output'
git switch 'd4nkn3ss-fix/forge-unicode-output'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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
MagicBeanDev/Longshot!120
No description provided.