mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description Fixes #1126. On a Windows (cp1252) locale, `headroom wrap` crashes with `UnicodeDecodeError` the first time it injects guidance into a user instruction file that contains non-ASCII prose (e.g. typographic quotes `“happy places”` or an em-dash). `_inject_rtk_instructions` and `_inject_memory_agents_md` both read the existing file and append/create it with a bare `read_text()` / `open()` / `write_text()`, so the default codec (cp1252, not UTF-8) chokes on the multi-byte characters. This is the same bug class already fixed for the `learn` pipeline (#1202) and earlier for other wrap paths — here it's the instruction-file injectors. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - `headroom/cli/wrap.py`: in `_inject_rtk_instructions` and `_inject_memory_agents_md`, read the existing instruction file as `encoding="utf-8", errors="replace"` and append/create with `encoding="utf-8"`. The read only feeds the marker-existence check and the append doesn't rewrite existing bytes, so replacement can't corrupt the file. - `tests/test_cli/test_wrap_encoding.py`: new regression tests. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check`) - [ ] Type checking passes (`mypy headroom`) - [x] New tests added for new functionality - [x] Manual testing performed ### Test Output ```text $ pytest tests/test_cli/test_wrap_encoding.py tests/test_cli/test_wrap_hintfile_agents.py -q 16 passed $ ruff check headroom/cli/wrap.py tests/test_cli/test_wrap_encoding.py All checks passed! ``` The new tests are **red on the old code, green with the fix**: injecting into a file with a typographic quote plus a stray `0x9d` byte (undefined in cp1252 and invalid UTF-8, so a bare `open()` fails on any locale) — the append and idempotent paths fail before the fix (4 failed) and pass after (6 passed). ## Real Behavior Proof - Environment: Windows 11, Python 3.10, against the real `headroom.cli.wrap` injectors (no live agent launch; the decode failure is at file read time). - Exact command / steps: `write_bytes` an `AGENTS.md` containing `"Be in “happy places” — really.\n"` plus a stray `0x9d` byte, then call `_inject_rtk_instructions(path)` / `_inject_memory_agents_md(path)`. - Observed result: **before** the fix → `UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9d` (and on a real cp1252 locale, the same on the typographic quotes alone); **after** → both return `True`, the marker is present, the pre-existing prose is preserved, and re-running is idempotent. - Not tested: a full end-to-end `headroom wrap copilot` against a live Copilot CLI (verified at the injector level, which is where the decode crash lives). ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review Co-authored-by: JerrettDavis <mxjerrett@gmail.com> |
||
|---|---|---|
| .. | ||
| conftest.py | ||
| test_copilot_auth.py | ||
| test_init_cli.py | ||
| test_init_enable_tool_search.py | ||
| test_install_cli.py | ||
| test_main_help_version.py | ||
| test_mcp.py | ||
| test_mcp_status.py | ||
| test_serena_disable.py | ||
| test_serena_migrate.py | ||
| test_subprocess_utf8_encoding.py | ||
| test_tokensave_helpers.py | ||
| test_tokensave_setup.py | ||
| test_unwrap_claude.py | ||
| test_wrap_aider.py | ||
| test_wrap_bridge.py | ||
| test_wrap_claude_base_url.py | ||
| test_wrap_claude_finally_unbound.py | ||
| test_wrap_claude_vertex_proxy_env.py | ||
| test_wrap_codex.py | ||
| test_wrap_continue.py | ||
| test_wrap_copilot.py | ||
| test_wrap_encoding.py | ||
| test_wrap_goose.py | ||
| test_wrap_helpers.py | ||
| test_wrap_hintfile_agents.py | ||
| test_wrap_openclaude.py | ||
| test_wrap_openclaw.py | ||
| test_wrap_opencode.py | ||
| test_wrap_openhands.py | ||
| test_wrap_persistent.py | ||
| test_wrap_proxy_detach.py | ||
| test_wrap_rtk_metrics.py | ||
| test_wrap_rtk_on_path.py | ||
| test_wrap_stale_marker.py | ||
| test_wrap_vibe.py | ||
| test_wrap_zcode.py | ||