gstack-jsonl-merge: non-UTF-8 redirected Python stdout fails on Unicode JSONL and emits CRLF on Windows

オープン 初心者向け
#3,659 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@garrytan がすでに取り組んでいます。

2026年10月31日 から。

  • #3714 @garrytan による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
82/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
git, python, shell
領域
tooling

調査の方向性

bin/gstack-jsonl-mergeから始め、特にマージしたレコードをstdoutに書き込む95行目付近のPythonブロックを確認してください。提供された非ASCIIのJSONL入力を使ってWindowsで再現し、Unicode出力と改行の両方を対象とするリグレッションテストを追加してください。有効なUTF-8入力が正常にマージされ、すべてのプラットフォームで出力がUTF-8かつLF改行のままであれば完了です。

索引モデルが issue の本文から書いたものです。

説明

S2

What happened

On Windows, bin/gstack-jsonl-merge (the jsonl-append merge driver) failed while git re-applied an autostash in ~/.gstack (git pull --ff-only --autostash). Git reported the file as conflicted (UU projects/<slug>/learnings.jsonl) with no conflict markers. Running the driver by hand on the three index stages printed:

Traceback (most recent call last):
  File "<stdin>", line 44, in <module>
  File "...\Lib\encodings\cp1250.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 273-286: character maps to <undefined>

The driver reads every input with encoding='utf-8', but writes the result with print(line) to its stdout (bin/gstack-jsonl-merge:95), which is redirected to the temp file (python3 - ... > "$TMP", line 51). In Windows environments where redirected Python stdout uses a legacy code page (cp1250 in this reproduction, with no PYTHONUTF8 or PYTHONIOENCODING set), print() cannot encode a JSONL line with a character the code page lacks, such as Cyrillic text in a learning, and raises UnicodeEncodeError. The script then exits 1 (lines 98-101), and git treats the merge as a conflict. The driver's header says exit 1 makes "git treats as conflict and stops the merge" (line 25), so this is the documented failure path, but the reproducer's input is valid UTF-8 JSONL.

A second effect of the same print(): on Windows, text-mode stdout writes CRLF, so even a successful merge rewrites LF input as CRLF, a full-file line-ending diff. We observed this on our Windows clone: merges through this driver turned three LF files of the artifacts repo into CRLF files, and the sync pushed them. No records were lost or doubled (the driver reads its inputs in text mode, which folds \r\n to \n), but the files can flip between LF and CRLF when merges run in environments with different stdout newline behavior. We restored LF by hand.

Expected: the driver writes UTF-8 with LF line endings whatever the platform and code page, and the merge succeeds. Possible fixes, as suggestions: write UTF-8 bytes with an explicit b"\n" to sys.stdout.buffer, or call sys.stdout.reconfigure(encoding='utf-8', newline='\n') at the top of the Python block. The environment variable PYTHONIOENCODING=utf-8 alone fixes the encoding but still writes CRLF on Windows: setting it by hand for the git command made the same merge succeed here, with CRLF output. A regression test with a non-ASCII line would cover it.

The crash needs a non-UTF-8 encoding for redirected Python stdout; the CRLF output happens with any Windows Python. Both occur when a real three-way merge or a stash re-apply in the artifacts repo invokes the configured jsonl-append driver: the brain-sync retry merge (bin/gstack-brain-sync:1382, when local and remote have diverged), and a user-run git pull or --autostash. Skill start's git merge --ff-only (bin/gstack-skill-start:440) does not run the driver when it fast-forwards.

Steps to reproduce

Windows, Git Bash, a Python whose locale.getpreferredencoding() is not UTF-8 (here cp1250). Synthetic data.

GS=~/.claude/skills/gstack; W=$(mktemp -d); cd "$W"
printf '{"ts":"2026-01-01T00:00:00Z","note":"base"}\n' > base; cp base ours; cp base theirs
printf '{"ts":"2026-01-02T00:00:00Z","note":"ours"}\n' >> ours
printf '{"ts":"2026-01-03T00:00:00Z","note":"\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82"}\n' >> theirs
bash "$GS/bin/gstack-jsonl-merge" base ours theirs; echo "exit=$?"; wc -l < ours
PYTHONIOENCODING=utf-8 bash "$GS/bin/gstack-jsonl-merge" base ours theirs; echo "exit_utf8=$?"; wc -l < ours

Output (traceback trimmed to its last line):

UnicodeEncodeError: 'charmap' codec can't encode characters in position 37-42: character maps to <undefined>
exit=1
2
exit_utf8=0
3

Readiness report

gstack doctor 1.91.318.0
ok              install root    ~/.claude/skills/gstack: agy global current (1.91.318.0), codex global current (1.91.318.0), claude global current (1.91.318.0)
ok              rescue refs     none in ~/.claude/skills/gstack
ok              state root      ~/.gstack durable=yes (selected by default)
ok              bun             1.4.4 (floor 1.3.3, tested 1.4.2)
ok              hooks           9 hook(s) parse
ok              windows bash    C:/Program Files/Git/bin/bash.exe (chosen by git --exec-path)
ok              codex           codex-cli 0.163.0; self-locate ok
ok              codex probe     cached MODEL_OK, 1h ago (not re-run; --live probes now)
warn            artifacts sync  status error
                                fix: ~/.claude/skills/gstack/bin/gstack-brain-sync --status
ok              browse bundle   browse/dist built
ok              binaries        find-browse, design, pdf, gstack-global-discover launch (4 of 4)
not configured  cso             native helper not built; /cso reports not assessed
ok              claude code     2.1.314 (Claude Code)
ok              session journal largest of 58: 19.3 MiB
ok              guard log       34 decisions (2 denied)
ok              CLAUDE.md       no gstack section lists skills
14 ok, 1 warn, 1 not configured, 0 fail

(The artifacts sync warning is the companion issue below, taken before it was repaired.)

Environment

  • Host (Claude Code, Codex, Cursor, ...) and its version: Claude Code 2.1.314
  • OS: Windows 11, Git for Windows 2.46.0, Python 3.12.6 (sys.stdout.encoding for a redirected stdout and locale.getpreferredencoding() are both cp1250).
  • gstack 1.91.318.0 (1bd2330); bin/gstack-jsonl-merge has the same blob at main 53522b1, where the lines above were checked.
  • Related: companion issue: #3658 (a brain-sync clone that is only behind origin is refused by the case-collision check).
主要言語
TypeScript
スター
138k
フォーク
20.5k
平均マージ
9時間 4分
マージ済み PR(30日)
364

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

garrytan/gstack のほかの issue

garrytan/gstack の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。