Skip to content

fix(step7): decode a tool's bytes by the code page that wrote them - #352

Open
PhysShell wants to merge 2 commits into
claude/p-022-analysis-wiring-mwhqlwfrom
claude/p022-step7-tool-encoding
Open

PhysShell wants to merge 2 commits into
claude/p-022-analysis-wiring-mwhqlwfrom
claude/p022-step7-tool-encoding

Conversation

@PhysShell

Copy link
Copy Markdown
Owner

Pre-measurement hygiene only. No protocol, no threshold, no authorization.

The defect

_tool() ran every probe through subprocess.run(..., text=True), which decodes with the locale's preferred encoding. On a non-English Windows that is the ANSI code page, while a console tool writes in the console output code page. They disagree, so powercfg arrived as mojibake — and mojibake whose bytes move with the ambient code page. One machine here resolves 65001 under PowerShell and 866 under Git Bash.

power_policy is identity-bearing and step-7 fields compare whole, so the same unchanged machine produces two different identity values. That is drift the capture invents rather than observes, and it would invalidate a stratum mid-campaign. python, rustc, dotnet and the CIM-backed cpu_model / virtualization all read through the same helper.

What was proved, before and after

The same text, emitted in three code pages:

locale preferred encoding here: cp1251
  cp866   OLD -> '‚лб®Є\xa0п Їа®Ё§ў®¤ЁвҐ«м\xad®бвм'
  cp1251  OLD -> 'Высокая производительность'      <- the accidental pass
  utf-8   OLD -> 'Высокая производительность'
  cp866/cp1251/utf-8  NEW -> 'Высокая производительность'  OK

The invariant is not that the bytes are UTF-8 — they are whatever the console is set to. It is that the decoded value is the same string whichever code page produced it.

The fix

_tool_encoding() asks Windows for the producing code page (GetConsoleOutputCP, then GetOEMCP) and decodes with it, UTF-8 elsewhere, with the same errors="replace" _text already uses. envcapture-tool-encoding drives one text through cp866, cp1251 and utf-8 and requires one string out of all three; against the old implementation it reports three.

No digest moves. The capture tool is outside all three frozen source sets and envcapture-frozen-untouched still passes. 12 controls pass, 0 fail; --selftest exits 0.

The note's Controls block is a live inventory, so it names the new control. The mutation paragraph beneath it is history and is left alone — the new control postdates those fifteen mutations and says so rather than borrowing their score.

What this PR does NOT do

No T0 / D7 policy, no statistic, no threshold, no aggregation rule, no host qualification, and no step-7 authorization. Step 7 stays blocked.

🤖 Generated with Claude Code

https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh

`_tool` ran every probe through `subprocess.run(..., text=True)`, which decodes
with the locale's preferred encoding. On a non-English Windows that is the ANSI
code page, while a console tool writes in the CONSOLE output code page. The two
disagree, so `powercfg` arrived as mojibake — and mojibake whose bytes move with
the ambient code page, which on one machine here resolves to 65001 under
PowerShell and 866 under Git Bash.

`power_policy` is identity-bearing and fields compare whole, so the same
unchanged machine produced two different identity values: drift the capture
invents rather than observes. `python`, `rustc`, `dotnet` and the CIM-backed
`cpu_model` and `virtualization` all read through the same helper.

The invariant is not that the bytes are UTF-8 — they are whatever the console is
set to. It is that the decoded VALUE is the same string whichever code page
produced it. `_tool_encoding()` asks Windows for the producing code page
(GetConsoleOutputCP, then GetOEMCP) and decodes with it, UTF-8 elsewhere, with
the same `errors="replace"` `_text` already uses.

`envcapture-tool-encoding` drives the same text through cp866, cp1251 and utf-8
and requires one string out of all three. Before this change that control reports
three different values; the accidental pass is cp1251, which is why a Russian
Windows could look fine in one shell and drift in the next.

No digest moves: the capture tool is outside all three frozen source sets, and
`envcapture-frozen-untouched` still passes. 12 controls pass, 0 fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
The Controls block is a live inventory, not a record of what the campaign once
ran, so omitting `envcapture-tool-encoding` would make it false. The mutation
paragraph beneath it is history and is left alone: the new control postdates
those fifteen mutations and says so rather than borrowing their score.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

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

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 1eff57f6-215a-4c33-8dc1-1387958c5764


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

Comment @coderabbitai help to get the list of available commands.

@PhysShell
PhysShell changed the base branch from main to claude/p-022-analysis-wiring-mwhqlw September 15, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants