Skip to content

fix: HTTP レスポンスの charset 判別を修正し文字化けを解消 + 保存済みデータ修復 CLI - #345

Merged
kentoku24 merged 5 commits into
mainfrom
fix/mojibake-http-decode
Aug 14, 2026
Merged

kentoku24 merged 5 commits into
mainfrom
fix/mojibake-http-decode

Conversation

@kentoku24

Copy link
Copy Markdown
Owner

概要

Discord daily notification に出ていた日本語タイトルの文字化け(例: 科学的に存在しうるクリーチャー娘の観察日誌 → ç§å¦ç…)を修正します。あわせて、保存済み state の化けたタイトルを一括修復する CLI を追加します。

原因

manga_watch/sources/base.py の RequestsHttpClient.get_text() が response.text を返しており、championcross.jp の series RSS が Content-Type: text/xml(charset なし)で配信されるため、requests が UTF-8 を ISO-8859-1 として誤デコードしていました。

変更内容

  • fix(sources): decode HTTP responses with explicit charset fallback — decode_response_bytes() を追加し、charset 宣言 → strict UTF-8(BOM 除去)→ apparent encoding → latin-1 の順で明示デコード。全ソース共通の修正
  • test(sources): add byte-level charset regression for champion-cross RSS — 実フィクスチャバイト + text/xml ヘッダ経由の回帰テスト(latin-1 誤デコードで red になることを確認済み)
  • feat: add mojibake repair for stored state titles — repair_mojibake.py(Latin-1 逆変換 + CJK 判定の安全ヒューリスティック、pending_messages の content は対象外)
  • feat(repair): add repair_mojibake CLI with dry-run and json output — --state / --backend {json,firestore} / --dry-run / --json 対応 CLI + README 手順

検証

  • 全 unittest: 504 tests OK(pre-existing skips 2)
  • live 実証: championcross.jp RSS から「科学的に存在しうるクリーチャー娘の観察日誌」/「第112話 ノソイ4」を正しくデコード・保存(mojibake 0、エラー 0)
  • repair CLI: dry-run(ファイル不変)→ apply → 冪等 → エラー exit 1 を実機確認
  • 検証ログ: .omo/evidence/task-{1,2,3,4}-fix-mojibake-http-decode.txt / f3-fix-mojibake-http-decode.txt

運用上の注意(本番適用時)

保存済み state の修復は README の手順に従い、まず --dry-run --json で対象確認 → state をバックアップ → apply してください。GCP 本番は --backend firestore です。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9381ab6185

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread manga_watch/sources/base.py Resolved
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@kentoku24

Copy link
Copy Markdown
Owner Author

$gh-pr-reviewer レビュー結果

$gh-pr-reviewer として PR #345 (fix/mojibake-http-decode → main) をレビューしました。レビューは chat 上の主張ではなく、実コード・テスト実走・evidence の一次確認に基づきます。

検証実施内容(本レビューで実走)

  • HEAD 3c4e93b の作業ツリーで .venv/bin/python -m unittest tests.test_check tests.test_repair_mojibake tests.test_sources → 144 tests OK(出力: .omo/evidence/reviewer-pr345.txt)
  • CI (unittest workflow) は tip commit 3c4e93b で success(5 コミット目の再実行が通っている)
  • 差分読解: decode_response_bytes() の fallback 順序(declared charset (errors=replace) → strict UTF-8 (BOM 除去) → apparent encoding (errors=replace) → latin-1 (errors=replace))を確認。response.text は消滅し、charset なし text/xml の誤 ISO-8859-1 デコードが根絶されている
  • evidence 確認: task-1〜5(red/green 証明・mutation check・P2 回帰の red 証明・full suite 508 OK (skipped=2))と f3(live championcross.jp 実証: 科学的に存在しうるクリーチャー娘の観察日誌 / 第112話 ノソイ4 が clean、repair CLI dry-run→apply→冪等→失敗 path exit 1、tracked file 無変更)

Findings

  • [P1] なし
  • [P2] なし(Codex bot の P2 指摘は commit 3c4e93b で実装解消済み。下記 Chief Engineer Comment Resolution 参照)
  • 軽微な注意(blocker ではない): PR Description の「全 unittest: 504 tests OK」は最終スイートでは 508 OK に増えている(task-5 の回帰テスト追加分)。数字のズレは記述時点と確定時点の差で、実装上の問題ではない。

Description Check

  • 「HTTP レスポンスの charset 判別修正による文字化け解消」: 実現済み — decode_response_bytes() の明示デコード + 実フィクスチャバイト経由の回帰テスト (test_champion_cross_rss_fixture_decodes_clean_japanese_titles) で裏取り済み
  • 「保存済み state の化けタイトルを一括修復する CLI 追加」: 実現済み — repair_mojibake.py(保守的ヒューリスティック: latin-1 round-trip + CJK 判定、pending_messages の content 除外)+ --state/--backend/--dry-run/--json CLI + README 手順(+2 行)
  • 「検証: 全 unittest / live 実証 / repair CLI 実機確認」: 実現済み — 本レビュー実走 144 OK、f3 live QA、task-1〜5 evidence で整合

Chief Engineer Comment Resolution

  • 「Preserve declared charset on malformed bytes (P2, base.py:75)」: 解消 — declared charset / apparent encoding を errors="replace" でデコードし、選択したエンコーディングを 1 バイトの不正バイトで破棄しない実装(未知コーデックの LookupError のみフォールスルー)。回帰テスト test_decode_response_bytes_declared_charset_with_malformed_byte_keeps_charset(あいう 保持 + U+FFFD 挿入)が追加され、task-5 evidence で red(修正前: あいう 欠落)→ green(508 OK)が証明されている。thread 返信のみでなく実装として納得できる形で解消

Repo Fit Check

  • Scope fit: OK — 変更は計画どおり 9 ファイルのみ(base.py / repair_mojibake.py / test_check.py / test_repair_mojibake.py / README.md +2 行 / evidence task-1〜5)。watchlist・state・spec・fixture への変更なし
  • Outcome fit: OK — 原因(charset なし text/xml を ISO-8859-1 誤デコード)に対する修正が全 source 共通の RequestsHttpClient.get_text() に効いており、live 実証で所望のアウトカム(タイトル clean)を確認
  • Validation fit: OK — 変更リスク(全 source の fetch 経路)に見合う検証。red/green 両方向 + mutation check + full suite 508 OK + CI green + live QA。ネットワーク依存の変更にもかかわらず一次根拠が十分
  • Change shape: OK — 小さく焦点の合った変更。repair CLI は独立モジュール (172 行) で既存 storage 契約 (get_state_path/load_state/save_state) を再利用し、repo 規模に対して過剰な抽象化なし。README に運用手順(dry-run → backup → apply、firestore backend はローカル未テストという明示的な caveat 付き)が追記されている

Decision

  • APPROVE
  • Reason: 文字化けの根因がコード上で特定・除去され(response.text 排除)、全 source 共通経路で fallback デコードに置換されている。Codex bot の P2 指摘は実装 + 回帰テスト + red/green 証明で解消済み。scope は計画どおり 9 ファイルで repo fit に無理がなく、本レビュー実走 144 tests OK・CI green・live QA の一次根拠が揃っている。merge blocker なし
  • Alternative path if NG: 該当なし

Residual Risks

  • declared charset に不正バイトが含まれる feed では U+FFFD が title に入りうる(意図した仕様)。実害が出るのはパーサが U+FFFD 入り title を検知不能にする場合だが、現行パーサ・live feed では発生せず、charset なし応答は strict UTF-8 probe が先に走るためリスクは限定的
  • apparent_encoding は requests のヒューリスティック依存。ただし errors=replace で crash はせず、latin-1 への最終フォールバックより優先されるだけ
  • repair CLI の firestore backend はローカル未テスト(README に明記済み)。本番適用は README 手順どおり dry-run → backup → apply を守れば rollback 可能な運用

最終判定

APPROVE

@kentoku24

Copy link
Copy Markdown
Owner Author

Merger Result

  • PR state: OPEN
  • Draft: no
  • PR Reviewer approval comment: ok (issuecomment-5293384982, $gh-pr-reviewer decisive APPROVE, 2026-08-14)
  • Review threads: all resolved (Codex bot P2 thread PRRT_kwDORF30-86Ynxnf was the only unresolved thread — addressed by commit 3c4e93b + owner reply; resolved by merger before gate)
  • Merge state: CLEAN
  • Merge requested: true
  • Decision: APPROVE
  • Reason: PR fix: HTTP レスポンスの charset 判別を修正し文字化けを解消 + 保存済みデータ修復 CLI #345 (fix/mojibake-http-decode → main) は OPEN / draft なし / mergeStateStatus CLEAN。$gh-pr-reviewer の decisive APPROVE (issuecomment-5293384982) を PR 上で確認。唯一 unresolved だった Codex bot P2 thread (base.py:75, declared charset 保持) は commit 3c4e93b + 回帰テスト + owner reply で対応済みのため merger が resolve し、再取得で全 thread resolved を確認。merge blocker なし。
  • Next action: gh pr merge 345 --merge --delete-branch=false で merge する(merge:true 明示)。

$merger
APPROVE

@kentoku24
kentoku24 merged commit e6cd23e into main Aug 14, 2026
1 check passed
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.

1 participant