fix(session-inbox-monitor): force UTF-8 on Stop-hook delivery + writer guide v0.2.1

Closes session-inbox-monitor-stophook-utf8-fix. Cyrillic message bodies arrived
as mojibake when force-delivered via the Stop-hook block reason.

Root cause: ~/.claude/hooks/stop-dispatcher.ps1 reads bodies with -Encoding UTF8
(fine) but did NOT set [Console]::OutputEncoding, so ConvertTo-Json to stdout
under a harness-spawned redirected pipe (WinPS 5.1) emitted in OEM cp866.

Fix applied to the machine-local hook (not in git): set
[Console]::OutputEncoding/$OutputEncoding = UTF8 at the top. In-situ RED->GREEN
verified through the real Stop-hook path: a Cyrillic pangram that previously came
back as mojibake now delivers clean; no-loop holds.

Repo changes: SKILL.md Failure modes documents the encoding contract for inbox
writers (no-BOM UTF-8 LF; WriteAllText, not Set-Content -Encoding utf8 which BOMs
under 5.1); bump 0.2.0 -> 0.2.1 PATCH. stop-dispatcher.ps1 multi-machine
propagation is the workshop setup's concern (notified).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 11:06:00 +03:00
parent d91809bd71
commit 29d5e9ffa8
2 changed files with 18 additions and 5 deletions

View File

@@ -1223,7 +1223,7 @@ Acceptance 1-6 all met by test evidence (not by inspection). Per-task detail in
---
## [session-inbox-monitor-stophook-utf8-fix] — Stop-хук портит UTF-8/кириллицу при принудительном вручении письма (inject через `decision:block`). Найдено при `-stophook-blockfix-proof` 2026-06-17 (независимо подтверждено workshop по байтам).
## 🟢 [session-inbox-monitor-stophook-utf8-fix] — Stop-хук портит UTF-8/кириллицу при принудительном вручении письма (inject через `decision:block`). Найдено при `-stophook-blockfix-proof` 2026-06-17 (независимо подтверждено workshop по байтам).
**Симптом:** кириллическое тело письма приходит в block-reason как mojibake; английский текст инструкции чист.
**Диагноз (verified):** (1) файл на диске — валидный UTF-8 (Get-Content -Encoding UTF8 читает чисто). (2) `~/.claude/hooks/stop-dispatcher.ps1` **читает** тело как `-Encoding UTF8` (стр. 44) — read-сторона ОК. (3) НО stop-dispatcher **не выставляет** `[Console]::OutputEncoding` → `ConvertTo-Json` в stdout при harness-spawn (redirected pipe, WinPS 5.1) кодируется в дефолтном OEM-codepage (cp866 на RU-Windows) → mojibake на выводе. (4) Репро через `Start-Process -RedirectStandardOutput` НЕ воспроизводит (наследует UTF-8 от родителя) → баг харнесс-spawn-специфичен; верифицируется только in-situ (реальный Stop-хук + кириллическое письмо). (5) Писательская сторона: `Set-Content/Out-File -Encoding utf8` под 5.1 сажает BOM (`EF BB BF`) + смешанные EOL — некcanon; Write-тул пишет чистый no-BOM UTF-8 LF.
@@ -1236,12 +1236,15 @@ Acceptance 1-6 all met by test evidence (not by inspection). Per-task detail in
NB: `stop-dispatcher.ps1` — machine-local (вне git, владелец — workshop-сетап). Правка требует ОК user (shared hook, влияет на Stop во всех проектах машины). ASCII-clean в `inbox-monitor.ps1` — это обход того же encoding-класса, не корень; корень здесь — read/write/output codepage.
**Weight:** needs-claude
**Status:** ready
**Where I stopped:** Диагноз полный (verified + workshop-confirmed). Не применял фикс — это shared machine-local hook, нужен ОК user; верификация только in-situ.
**Next action:** По ОК user: добавить OutputEncoding=UTF8 в stop-dispatcher.ps1 (бэкап), прислать кириллическое тест-письмо, end turn → проверить что forced-delivery чист. Затем writer-гайд в SKILL.md. Findings → review.
**Status:** done
**Where I stopped:** Шипнуто+верифицировано 2026-06-17 (ОК user). (1) `[Console]::OutputEncoding = [System.Text.Encoding]::UTF8` (+`$OutputEncoding`) добавлено в начало `~/.claude/hooks/stop-dispatcher.ps1` (бэкап `.bak-utf8fix`; parse 0 ошибок; BOM файла сохранён → русские комменты целы). (2) Writer-гайд (no-BOM/LF, WriteAllText vs Set-Content) задокументирован в SKILL.md Failure modes, bump 0.2.0→0.2.1 PATCH. (3) **In-situ regression PASS:** кириллический pangram «Съешь же ещё этих мягких французских булок…» прислан через реальный inject → block-reason пришёл ЧИСТО (до фикса то же тело давало `<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>`); no-loop подтверждён. Root-cause = output-encoding (read был уже UTF8), как диагностировано; Start-Process-репро баг не ловил — только реальный harness-spawn путь. NB: stop-dispatcher.ps1 machine-local (вне git) — фикс на этой машине; для multi-machine надо внести в workshop-сетап, что шипает хук (нотифай ушёл).
**Next action:** (none — kept until merged). Multi-machine propagation фикса stop-dispatcher — на стороне workshop-сетапа.
**Branch:** n/a
**Notify:** OpeItcLoc03/workshop
<!-- created-by: vitya@claude-skills / from: -stophook-blockfix-proof finding + workshop encoding-report / 2026-06-17 -->
<!-- closed-by: vitya@claude-skills / 2026-06-17 / OutputEncoding=UTF8 applied to stop-dispatcher.ps1, in-situ Cyrillic regression PASS, SKILL.md writer-guide v0.2.1 -->
<!-- amg: stop-dispatcher.ps1 machine-local, fix not in git; workshop owns multi-machine propagation -->
---