背景
アーキテクチャレビュー(2026-07-02)の G-2/G-3/G-4 と監査(2026-06-20)の L-31/L-32/L-34/L-36/L-37 で指摘済みの構造負債を、挙動を一切変えない リファクタリングとして解消する。receive.js は 2083 行(export ゼロ)、widget/src/index.js は 1729 行(state 直接参照 180 箇所)まで肥大している。
スライス(PR 分割)
R-1: DRY 小粒 + build ガード(refactor/dry-core)
safeFilePart の重複解消(receive.js:1499 と widget index.js:970 にバイト同一)→ shared/format.js へ移設。まず server 側を切替(widget 側の切替は R-3 で実施)
status 定数の単一ソース化: FEEDBACK_STATUSES(receive.js:98)と VALID_STATUSES(store.js:41)→ store.js から export し receiver が require。正規化の丸め挙動(不正値→"new")は不変のまま (拒否化は L-12 で別 issue)
build.js に複数宣言子 export(export const A = 1, B = 2;)の検出 fail を追加(L-34。現 src 該当 0 件なので dist 不変)
R-2: inbox view の切り出し(refactor/inbox-view、R-1 の上に stack)
renderInbox 系 7 関数(receive.js:1904-2083)+ renderLoginPage を server/inbox-view.js(CJS)へ。G-2 で「最も安全・いつでも切れる」と合意済みの境界
レスポンスヘッダを書く responder(respondLoginPage / handleGetInbox 等)は receive.js に残す。HTML 出力はバイト単位で不変
R-3: widget コアの分割(refactor/widget-split)
state + DEFAULTS を widget/src/state.js へ(state への再代入はゼロ・全 180 箇所がプロパティ読み書きなので参照共有で挙動不変)
state 非依存の純関数クラスタを分割: screenshot SVG 生成系 / 永続化 envelope 系 / payload 組み立て。orchestrator(submitComment / renderShell / renderFeedbackList / restorePersistedFeedback 等の相互再帰群)は index.js に残す (build.js は循環 import で hard fail)
切り出したモジュールに単体テストを追加(テスト負債: widget コア単体 / Slack 配信経路 / import 失敗系分岐の欠落 #86 の widget コア単体テスト不在と同方向のスライス)
widget 側 safeFilePart を shared 参照へ切替(L-31 完了)
触らない境界(挙動変更 or 進行中 issue との衝突)
受け入れ条件(各 PR 共通)
npm run check(lint + dist バイト一致 + 全テスト)green。既存テストの期待値は 1 件も変更しない
widget を触る PR は dist 再生成をコミットし、dist の diff で関数本体のテキストが不変 (モジュール境界コメント・IIFE ラッパ以外に差分がない)ことを確認
R-3 は実ブラウザ E2E(コメント投稿 → receiver 保存)で回帰がないことを確認
AI 相互レビュー(author ≠ reviewer)で must 0
実装分担: R-1/R-2 は Codex、R-3 は Claude(レビューは各々反対側)。
背景
アーキテクチャレビュー(2026-07-02)の G-2/G-3/G-4 と監査(2026-06-20)の L-31/L-32/L-34/L-36/L-37 で指摘済みの構造負債を、挙動を一切変えないリファクタリングとして解消する。receive.js は 2083 行(export ゼロ)、widget/src/index.js は 1729 行(state 直接参照 180 箇所)まで肥大している。
スライス(PR 分割)
R-1: DRY 小粒 + build ガード(
refactor/dry-core)safeFilePartの重複解消(receive.js:1499 と widget index.js:970 にバイト同一)→shared/format.jsへ移設。まず server 側を切替(widget 側の切替は R-3 で実施)FEEDBACK_STATUSES(receive.js:98)とVALID_STATUSES(store.js:41)→ store.js から export し receiver が require。正規化の丸め挙動(不正値→"new")は不変のまま(拒否化は L-12 で別 issue)export const A = 1, B = 2;)の検出 fail を追加(L-34。現 src 該当 0 件なので dist 不変)R-2: inbox view の切り出し(
refactor/inbox-view、R-1 の上に stack)renderInbox系 7 関数(receive.js:1904-2083)+renderLoginPageをserver/inbox-view.js(CJS)へ。G-2 で「最も安全・いつでも切れる」と合意済みの境界respondLoginPage/handleGetInbox等)は receive.js に残す。HTML 出力はバイト単位で不変R-3: widget コアの分割(
refactor/widget-split)state+DEFAULTSをwidget/src/state.jsへ(stateへの再代入はゼロ・全 180 箇所がプロパティ読み書きなので参照共有で挙動不変)safeFilePartを shared 参照へ切替(L-31 完了)触らない境界(挙動変更 or 進行中 issue との衝突)
gitHubIssueBody系(receive.js:847-955)→ payload に sourceContext(repo/branch/commit)を追加し、GitHub Issue body を agent-ready に再設計する(路線 1 の前提) #96 PR-B の再設計領域受け入れ条件(各 PR 共通)
npm run check(lint + dist バイト一致 + 全テスト)green。既存テストの期待値は 1 件も変更しない実装分担: R-1/R-2 は Codex、R-3 は Claude(レビューは各々反対側)。