Skip to content

widget: state と純関数クラスタを 4 モジュールに分割する (#109 R-3) - #112

Merged
kosako merged 2 commits into
mainfrom
refactor/widget-split
Jul 11, 2026
Merged

widget: state と純関数クラスタを 4 モジュールに分割する (#109 R-3)#112
kosako merged 2 commits into
mainfrom
refactor/widget-split

Conversation

@kosako

@kosako kosako commented Jul 11, 2026

Copy link
Copy Markdown
Owner

概要

#109 のスライス R-3(R-2 の上に stack。R-1/R-2 merge 後に main へ retarget)。挙動を一切変えない widget コアの分割。index.js は 1729 → 1311 行。

  • widget/src/state.jsDEFAULTSstate を移動。state への再代入はゼロ・全 180 箇所がプロパティ読み書き(実測)なので、オブジェクト参照共有で挙動不変
  • widget/src/screenshot.js — screenshot SVG 生成系 10 関数(export は captureScreenshot / byteLength / base64Encode のみ、他はモジュール私有)
  • widget/src/persistence.js — localStorage 永続化系 9 関数 + FEEDBACK_STORAGE_VERSION
  • widget/src/payload.js — buildPayload / generateFeedbackId(呼び出し元 1 箇所を実測確認して私有)+ PAYLOAD_SCHEMA_VERSION
  • orchestrator(submitComment / renderShell / renderFeedbackList / restorePersistedFeedback 等の相互再帰群)は index.js に残置(build.js は循環 import で hard fail するため、新モジュールは葉に徹する設計)
  • Slack 直送系(postSlackWebhook ほか)は Slack 経路の縮約: widget 直送モード削除と SLACK_IMAGE_MODE の 2 種化(link/off) #97 で削除予定のため不移動
  • safeFilePart を shared 参照に切替(R-1 で移設済み。監査 L-31 のバイト同一重複を解消)
  • 切り出しモジュールに単体テスト 11 件を追加(テスト負債: widget コア単体 / Slack 配信経路 / import 失敗系分岐の欠落 #86 の widget コア単体テスト不在と同方向)

検証

  • npm run check green(全 124 テスト: 既存 113 + 新規 11。既存期待値の変更なし)
  • dist の機械照合: ラッパ・境界コメント・import destructuring・空行を除く全 1912 行が新旧でバイト単位一致。top-level 宣言 140 個すべて本体テキスト一致(verbatim move の証明)
  • 実ブラウザ E2E: コメント投稿 → payload 組み立て(schemaVersion 2 / sourceContext / screenshot captured)→ receiver 201 → screenshot 保存、reload → localStorage 復元(旧ビルドが書いた envelope の復元も確認 = クロスバージョン互換)→ pin 再描画、まで回帰なし

実装: Claude / レビュー: Codex(相互レビュー契約)

🤖 Generated with Claude Code

https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau

@kosako

kosako commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

🔍 レビュー依頼(→ Codex)

  • 観点: 挙動維持の widget コア分割。重点は (1) verbatim move の忠実性(dist の関数本体不変)、(2) 循環 import・build.js 制約(単一行 import / 1 宣言 1 export)への適合、(3) state 参照共有の正しさ(live binding 喪失の影響有無)、(4) モジュール境界(export の最小性・葉モジュール性)、(5) Slack 経路の縮約: widget 直送モード削除と SLACK_IMAGE_MODE の 2 種化(link/off) #97 予定領域(Slack 直送)の不可侵、(6) 追加テスト 11 件の妥当性
  • ランク: 🔴 must / 🟡 should / ⚪ nit
  • 結果はこの PR にコメントで返します

@kosako

kosako commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

📋 レビュー結果(by Codex)

判定: ✅ merge 可(must 0 件)

🔴 must

なし

🟡 should

なし

⚪ nit

なし

レビュアーの確認事項(転記)

  • 移動対象の関数本体と定数・state 定義は旧実装と一致。dist 差分はモジュールラッパー・import・生成順の変更に限定
  • safeFilePart は同一実装の shared/format.js 参照へ変更
  • Slack 直送系 3 関数は index.js に残置(Slack 経路の縮約: widget 直送モード削除と SLACK_IMAGE_MODE の 2 種化(link/off) #97 境界の不可侵を確認)
  • state 本体の宣言は 1 箇所。各モジュールは同一オブジェクトを参照し、binding の再代入なし
  • 新モジュールから index.js への逆依存・循環 import なし。import/export は build.js の制約(単一行 named import・相対 .js・1 宣言 1 export)に適合
  • 追加 export は配線または単体テストから利用されており、孤立 export なし
  • 追加テスト 11 件は serialization / envelope 判定 / UTF-8・base64 / chunk 境界 / Blob fallback を具体値で検証
  • 実行: lint / build --check / widget+shared テスト 62 件 すべて成功(receiver テストは sandbox 制約で対象外、author 側で全 124 件 pass 済み)

作者側の追加証跡: 実ブラウザ E2E(投稿 → 201 → reload 復元 → pin 再描画、旧ビルド envelope のクロスバージョン復元含む)は PR 本文のとおり green。

@kosako
kosako marked this pull request as ready for review July 11, 2026 09:33
@kosako
kosako force-pushed the refactor/inbox-view branch from 9eb42c5 to de34fd3 Compare July 11, 2026 11:08
@kosako
kosako changed the base branch from refactor/inbox-view to main July 11, 2026 11:09
kosako and others added 2 commits July 11, 2026 20:10
widget/src/index.js (1729 行) から挙動を変えずにコードを一字一句そのまま
移動して 4 モジュールを新設する (verbatim move):

- state.js: DEFAULTS と state (オブジェクト参照共有。state への再代入は
  無く全参照がプロパティ読み書きのため挙動不変)
- screenshot.js: captureScreenshot / buildScreenshotSvg /
  screenshotOverlayFor / collectReadableStyles / visibleBackground /
  snapshotClassAttr / serializeAsXhtml / renderScreenshotOverlay /
  byteLength / base64Encode
- persistence.js: FEEDBACK_STORAGE_VERSION / loadStoredReviewer /
  saveReviewer / loadPersistedFeedback / persistFeedbackList /
  clearPersistedFeedback / feedbackStorageEnvelope /
  serializeFeedbackForStorage / normalizePersistedFeedback /
  isMatchingFeedbackEnvelope
- payload.js: PAYLOAD_SCHEMA_VERSION / buildPayload / generateFeedbackId
  (呼び出し元が buildPayload のみであることを grep で確認)

restorePersistedFeedback は index.js 残留関数 (removeCommittedMarkers /
restoreFeedbackMarkers) を呼ぶ orchestrator のため index.js に残す。
Slack 直送系 (postSlackWebhook ほか) は #97 で削除予定のため移動しない。
safeFilePart は R-1 スライスと競合させないため触らない。

新モジュールは全て「葉」(index.js への逆依存なし) で、dist の差分は
モジュール境界コメント・IIFE ラッパ・並び順・import 行のみ。全 1912 行の
関数本体テキストがバイト一致することを機械照合で確認済み。

node 環境で動く純関数に単体テストを追加:
- test/widget-persistence.test.js: serialize/normalize の round-trip と
  envelope 一致判定
- test/widget-screenshot.test.js: byteLength / base64Encode

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau
R-1 で shared へ移設済みの safeFilePart を widget 側でも import に切替え、
バイト同一の重複定義(監査 L-31)を解消する。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau
@kosako
kosako force-pushed the refactor/widget-split branch from d0eb132 to 242f041 Compare July 11, 2026 11:10
@kosako
kosako merged commit 1fa4e1b into main Jul 11, 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