Skip to content

fix(reports): preserve UTF-8 LF report artifacts on Windows - #7251

Open
JasonBuildAI wants to merge 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/agent-q8n4
Open

JasonBuildAI wants to merge 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/agent-q8n4

Conversation

@JasonBuildAI

@JasonBuildAI JasonBuildAI commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Every native scheduled report fails on supported Windows hosts, stranding an already-generated model response before private delivery can begin.

This affects the shipped, opt-in native scheduled-report capability introduced by #7179. The failure is deterministic even when Python runs in UTF-8 mode, and default non-UTF-8 Windows locales (cp936, cp932, cp949) independently fail on report-artifact reads. Native Windows is a documented supported surface, and recent cp936 regressions such as #6708 and #7063 show that this locale class has real users.

What is broken

freeze_private_report wrote report.md through text mode. Windows translated its LF line endings to CRLF, while private-generation.json hashed the in-memory LF body. The immediate validation then hashed the CRLF file and rejected its own generation. Because this happens inside the scheduled report pipeline, the report Todo cannot complete, private delivery never starts, and the same failure recurs for every report window.

Five report-artifact reads also relied on the host default codec: frozen request/source inputs, generation/delivery receipts, the generated body, and the full body returned to the original chat. Those reads fail on Windows hosts whose default encoding is not UTF-8.

Fix

  • Encode report.md once as UTF-8, write those exact bytes without newline translation, and use the same bytes for body_sha256.
  • Read and decode the frozen report bytes explicitly as UTF-8 during validation.
  • Pin UTF-8 on all five report-artifact reads.
  • Add a focused regression that drives the real cadence-window and private-generation entry points while simulating Windows newline translation and a non-UTF-8 host codec.

Reproduction and result

Before the fix, on native Windows with preferred_encoding=cp936 and utf8_mode=0:

receipt_body_sha256=8d25fc09e804067d76be2baf61acdf27f033aa46421e1336d7de93eadc1955a5
report_bytes_sha256=da269345ccdeef48696415b764a1d9d5809363a9a9cf0e225f3249599960fba2
report_lf_sha256=8d25fc09e804067d76be2baf61acdf27f033aa46421e1336d7de93eadc1955a5
report_has_crlf=True
freeze_status=failed exception=ValueError: frozen generation or full report body changed
default_read_exception=UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 4: illegal multibyte sequence

After the fix, through the same real entry point:

preferred_encoding=cp936
utf8_mode=0
freeze_status=succeeded generation_id=report_generation_3457b2044a6158f6567bacaf
receipt_body_sha256=8d25fc09e804067d76be2baf61acdf27f033aa46421e1336d7de93eadc1955a5
report_bytes_sha256=8d25fc09e804067d76be2baf61acdf27f033aa46421e1336d7de93eadc1955a5
report_has_crlf=False
report_has_utf8_non_ascii=True

The new regression fails against the parent commit with ValueError: frozen generation or full report body changed and passes with this change.

Validation

  • New native-Windows regression test: 1 passed.
  • Shipped-runtime UTF-8 read/write guard: 2 passed; it previously listed all five unpinned reads.
  • Rebased-head periodic-report, pending-intent, and UTF-8 guard suite under PYTHONUTF8=1: 150 passed in 230.96s.
  • ruff check on all four changed files: All checks passed!.
  • loopx canary premerge --from-git-diff: passed, selected=2, failures=0.
  • git diff --check, Python compile checks, public/private boundary scan, CLI output-budget smoke, and semantic-vocabulary drift smoke: passed.

Environment-only limits: two legacy native-steward tests could not execute on this Windows host because their fake codex executable is extensionless; test_periodic_report_machine_store and test_periodic_report_turn_replan fail identically on the clean parent commit for unrelated Windows/registry reasons. No passing result is claimed for those paths.

CI note at head a4fa4a1a: DCO, dependency review, Windows PowerShell, all TypeScript core jobs, stage2c, and the frontstage build pass. Three browser jobs and the release-artifact browser step fail on the UI assertion Goal drawer header is still pinned too close to the top edge; this change contains no frontend edits. kernel-contracts reports 5,236 repository-wide mypy errors, with no error on a changed line. The six Python test shards remained queued in the repository concurrency group at handoff, not blocked on workflow approval.

The change is limited to the write/read boundary and one focused test: 4 files, +188/-8. No dependencies, public contracts, or unrelated refactors are included.

Follow-up to #7179 and refined paths from #7200, #7215, and #7218.

AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.

Fixes #7246

Native scheduled reports wrote report.md through platform text mode, which
turned LF into CRLF on Windows while the frozen receipt hashed LF. Validate
the same UTF-8 bytes that were written, and pin UTF-8 reads for the frozen
inputs, health projection, and original-chat body handoff.

Add a report-generation regression test that simulates Windows newline
translation and a non-UTF-8 host codec.

Fixes loopx-project#7246

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
JasonBuildAI added a commit to JasonBuildAI/loopx that referenced this pull request Oct 24, 2026

This branch has not been deployed

No deployments
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.

[Bug]: native scheduled reports fail on Windows because report.md uses CRLF and artifacts are read with the host codec

1 participant