この repository の拡張は Chrome Web Store で公開済みです(v1.1.1、公開ページ最終更新 2026-06-18、オーナー確認 2026-07-06)。Phase 1 / Phase 1.5(local MV3 shell・popup・storage・synthetic fixture・F1-A research scaffold)、Phase 2 の production 機能、M7 の提出・公開は完了しています。
- production sync 実装済み: 宣言的
world:"MAIN"content script(/settings/blocked/all・/settings/muted/all限定)が、ユーザー自身のブロック・ミュート一覧 GraphQL 応答からuser_id(rest_id)/handle(screen_name)/listKindのみを抽出し、ISOLATED bridge 経由でchrome.storage.localの世代別同期 shard に取り込みます。本文を読む対象は x.com / twitter.com の/i/api/graphql/<query-id>/BlockedAccounts|MutedAccountspathname に厳密限定し、query / fragment に同名文字列がある無関係な応答は読みません。raw response・cursor 値・表示名・本文は保存しません。実アカウントで blocked 234件 / muted 50件の取り込みを確認済み(件数のみ・2026-06-13)。 - reconciliation 実装済み: 一覧の末尾(完全同期)に到達したときだけ当該 listKind を全置換し、解除済みアカウントを除去します。部分取得時は追加のみです(完全同期検出 = 抽出0件かつ Bottom cursor、
Storage.replaceSyncedListKind())。Top cursor だけの空ページは完了扱いしません。同一ページの再同期では cursor 無し initial request の固定sync-startだけが staging を新しい全走査へ切り替え、pagination / tail-only refetch は前回の完全集合を保持します。request variables / cursor 値は bridge へ送りません。 - real-DOM author matching 実装済み: 通常 content script が投稿カードの User-Name 領域に限定して投稿者を判定し、quote / embed の混在を分離します(引用カードは host 投稿を残したままその場で隠します)。実 TL で誤判定なく動作することを確認済み(M5)。
- popup から同期の有効化・ブロック / ミュート件数・最終同期時刻の確認・同期データ削除ができます。F1-A 観測メモ(開発用)は本番では非表示です(dev フラグ
RESEARCH_UI_ENABLED、既定 false)。 - 残作業: 公開後運用(不具合報告対応・X 側変更の追従。手順は
docs/review-response-playbook.md§3〜§4)と、オーナー未回答の要件確認(docs/requirements-v2-2026-07.md§7 Q3〜Q7)。2026-07-15 のレビュー所見3件はローカル再現と回帰検証を終えています。公開版は v1.1.1 で現行manifest.jsonと一致しています。docs の分類索引はdocs/README.mdを参照してください。
X/Twitter でブロック・ミュート済みアカウント由来の情報露出(RT・引用経由を含む)を減らすことを目指す Chrome 拡張です。データはすべて端末ローカル保存・外部送信なし・権限最小(storage + x.com / twitter.com host)を維持します。
- Chrome Manifest V3 の
manifest.jsonを作成する - 対象予定ドメインとして
https://x.com/*とhttps://twitter.com/*のhost_permissionsを宣言する - ローカル読み込み手順と開発上の注意を文書化する
- DOM フィルタ
- ストレージ層
- popup の表示や操作
- content script
- background service worker
- F1-A、F1-B、F1-C、F1-D の取得処理
webRequest、cookies、tabs、activeTab、<all_urls>、https://api.x.com/*などの追加権限
- popup でフィルタ ON/OFF、表示モード、登録件数、最終 synthetic test-data 更新時刻を表示する
- popup から決定的な Phase 1 synthetic test data を
chrome.storage.localへ投入、削除する - 設定を
chrome.storage.syncに保存する - content script がホーム TL 相当の投稿カードを監視し、
data-user-idまたはdata-handleが登録対象に一致したカードを処理する - 表示モードとして
hidden、placeholder、offを扱う tests/fixtures/home-timeline.htmlでログイン不要の synthetic 確認を行う
- F1-A / F1-B / F1-C / F1-D の一覧取得処理
- X API 連携、OAuth、Cookie、CSRF token、実アカウントデータ取得
- 本番用の
MAINworldfetch/XMLHttpRequesthook webRequest、cookies、tabs、activeTab、<all_urls>、https://api.x.com/*- import/export、options page
現在の content script の handle 抽出は、synthetic fixture とローカル Phase 1 確認のためのものです。実 X DOM では、投稿カード内のリンクや埋め込み要素が必ず投稿者本人を示すとは限りません。
- 実 X DOM の author identity は保証していません。
- quote / embedded target / profile card / 関連リンクの扱いは production-complete ではありません。
- 実 DOM で安全に投稿者を判定する author matching は Phase 2 以降の作業です。
- この Phase 1 / Phase 1.5 task では real-DOM 著者判定ロジックを変更しません。
この節は F1-A feasibility investigation 当時の履歴です。現行 v1.1 / M7 の出荷版では research UI と動的注入を retire し、manifest の permissions は storage のみです。本番同期は manifest の宣言的 world:"MAIN" content script で行うため、scripting 権限は使いません。
- F1-A feasibility investigation のため、
/settings/blocked/allと/settings/muted/allに限定した研究用 bridge を追加する - 当時は
chrome.scripting.executeScriptのworld: "MAIN"でfetch/XMLHttpRequesthook を入れる - hook は raw response、Cookie、CSRF token、token、raw user_id、raw handle、表示名、本文を保存しない
- sanitized observation は
xtbmF1AResearchに保存し、通常のxtbmEntriesには混ぜない - popup に
F1-A 観測メモ(開発用)、観測件数、ブロック / ミュート別の件数、安全な要約コピー、削除操作を追加する - popup から判定用の
masked summaryをコピーできる tests/scripts/evaluate-f1-observation.mjsで masked summary を機械判定できる- F1-A 採用条件と fallback 方針を docs に残す
以下は Phase 1.5 当時の非スコープです。現在は Phase 2 production sync / real-DOM author matching / reconciliation を実装済みで、未確認事項は後述の「検証状況」を正とします。
- 本番用 block / mute list sync
- captured response から
xtbmEntriesへ登録する処理 - F1-B DOM extraction
- F1-C X API / OAuth
- F1-D import UI
- raw X response、HAR、screenshot、Cookie、CSRF token、OAuth token、raw user_id、raw handle の保存
- Chrome で
chrome://extensionsを開く。 - 右上の
Developer modeを有効にする。 Load unpackedをクリックする。- この拡張のフォルダ(
manifest.jsonがある場所)を選択する。 x-true-block-muteが表示され、manifest エラーが出ていないことを確認する。- 拡張アイコンの popup を開き、
通常フィルタ、ローカル確認用データ、ブロック・ミュート同期が表示されることを確認する(F1-A 観測メモ(開発用)は本番では非表示。開発時に確認する場合はsrc/shared/constants.jsのRESEARCH_UI_ENABLEDをtrueにして拡張を再読み込みする)。 - 初心者向けの確認は
docs/manual-popup-verification.mdの手順に沿って行う。
popup の 詳細設定・プライバシー から設定ページ(src/options/options.html、options_ui で登録)を開けます。設定ページでは次を確認・操作できます。
- プライバシー説明: データは端末内(
chrome.storage.local)のみに保存され外部送信なし、取り込むのは user_id・handle・listKind のみ、権限はstorageと x.com / twitter.com host のみ。 - フィルタ対象の一覧(透明性): 同期で取り込んだブロック / ミュート対象の件数と一覧、ローカル確認用データの件数。
- 管理: 同期データの削除、テストデータの削除。
- 「うまく同期できないとき」のトラブルシュート手順(P2-015 のエラー日本語ガイダンス)。
宣言している権限は次だけです。
-
permissionsstorage
-
host_permissionshttps://x.com/*https://twitter.com/*
storage は popup・options・content script が設定、専用 xtbmSyntheticEntries の synthetic test data、世代別 xtbmSyncEntries:<generation> の本番同期対象、legacy/manual 行の xtbmBaseEntries、同期状態、削除競合を解決する非機密の世代・移行情報を共有するために使います。旧 single-key xtbmEntries は全領域の二段階移行が完了するまで読取互換を保ち、commit 後は専用領域を再書込みせず key 全体を削除します。本番同期は宣言的 world:"MAIN" content script で行うため scripting を必要としません。F1-A research の動的注入だけが scripting を使っていましたが、M7 で research を retire し scripting 権限を削除しました。研究の評価用スクリプト・テスト・判断記録(docs/decisions/f1-source-selection.md)はリポジトリに残しますが、出荷パッケージには含めません。
webRequest、cookies、tabs、activeTab、<all_urls>、https://api.x.com/*、scripting は宣言していません。
chrome.storage.sync:
- key:
xtbmSettings - value:
{ schemaVersion: 1, enabled: boolean, displayMode: "hidden" | "placeholder" | "off" }
chrome.storage.local:
- key:
xtbmEntries - value: 旧 single-key store。専用 base、legacy synthetic fallback、初期 sync shard の全書込みと移行 commit が成功するまで authoritative な読取元として残し、その後は stale snapshot を書き戻さず key 全体を削除する
- key:
xtbmBaseEntries - value:
{ schemaVersion: 1, entries: Entry[], lastSyntheticUpdatedAt: null }。旧 store にあった sync/synthetic 以外の legacy/manual 行だけを保持する独立領域。現行の全置換 API は公開しない Entry.user_idは存在する場合の primary key として扱うEntry.handleは補助キーとして扱うEntry.listKindは"blocked" | "muted" | null。同期で取得した一覧の種別を表す(schema v2 で追加。旧データ読み込み時はnull)Entry.syncedAtは同期で書き込んだ ISO 文字列、またはnull(schema v2 で追加)- Phase 1 synthetic entries は
source: "phase1-synthetic"とidResolutionStatusを持ち、専用xtbmSyntheticEntriesに保存する。本番同期 shard や legacy base を synthetic 操作から書き換えない - 本番同期で取り込むユーザー自身のブロック・ミュート対象は
source: "f1a-sync"を持つ。Storage.upsertSyncedEntries()が user_id 優先(handle 補助)で重複排除し、Storage.replaceSyncedListKind()が完全同期時に当該 listKind を全置換して解除済みアカウントを除去(部分取得時は追加のみ)、Storage.clearSyncedEntries()が同期分のみ削除する。別 JavaScript context の削除と同期が重なった場合は active generation を切り替え、開始済みの旧書込みを旧 shard だけから除去して削除を優先する。これらは端末内chrome.storage.localに限り、docs / commit には raw 値を出さない(詳細はdocs/privacy-threat-model.md) - key:
xtbmSyntheticEntries - value:
{ schemaVersion: 1, entries: Entry[], lastSyntheticUpdatedAt: string | null }。synthetic 行だけを保持する独立領域 - key:
xtbmLegacySyntheticEntries - value: 旧 store の synthetic 行を二段階移行時に退避する fallback。
xtbmSyntheticEntriesが存在する場合は現行の専用領域を優先する - key prefix:
xtbmSyncEntries: - value:
{ schemaVersion: 1, entries: Entry[], lastSyntheticUpdatedAt: null }。generation ごとに本番同期行だけを保持し、active shard だけを通常読取へ統合する。世代 marker が未設定の既存利用者は固定 initial shard を使う - key:
xtbmSyncGeneration - value:
{ generation: string | null }。popup/options の削除だけが更新する active shard pointer。settings page writer は世代を作らない。pointer 更新が削除の linearization point で、clear は prefix snapshot と live pointer の再読取により、その時点の active shard を除外して retired shard を再 cleanup する - key:
xtbmSyncMigrated - value:
boolean。xtbmBaseEntries、xtbmLegacySyntheticEntries、initial shard を書き終えた後だけ公開する移行 commit。いずれかの書込みまたは commit が失敗した場合は旧xtbmEntries全体を authoritative なまま残し、次回 mutation で再試行する - key:
xtbmSyncEnabled/xtbmSyncLastSyncedAt - value:
boolean/string。別 context の無効化と同期完了記録が互いを上書きしないようフィールド別に保存する。旧xtbmSyncStateは読取互換だけを維持する - key:
xtbmF1AResearch - value:
{ schemaVersion: 1, enabled: boolean, observations: Observation[], updatedAt: string | null } xtbmF1AResearch.observationsは endpoint class、top-level key、shape path、field presence、count、hook continuity marker だけを持つ masked research summary- raw response body、header、Cookie、CSRF token、Authorization header、OAuth token、raw user_id、raw handle、表示名、本文は schema 外
X にログインせず、実アカウントの投稿内容や Cookie を読まずに確認できます。
- 拡張 popup を開き、
ローカル確認用データのテストデータを入れるを押す。 登録済みの対象が0件以外になったことを確認する。tests/fixtures/home-timeline.htmlをブラウザで開く。説明だけ表示を押す。- 非対象の投稿だけ本文が残り、user_id 対象と handle-only 対象の投稿本文が中立プレースホルダに置き換わることを確認する。
完全に隠すを押す。- 対象投稿カードが表示領域から消え、非対象投稿が残ることを確認する。
何もしないを押す。- 置換済みカードが復元され、対象投稿本文が再表示されることを確認する。
- popup の
テストデータを消すを押す。 - 対象投稿が処理されない状態に戻ることを確認する。
popup で見る場所、件数の意味、貼ってよい情報、貼ってはいけない情報は docs/manual-popup-verification.md にまとめています。
Node.js が使える環境では、まず静的10本の一括検証を実行します。
node scripts/check-all.mjsこの検証は、manifest の権限、必須ファイル、JavaScript 構文、docs 整合、F1-A safety、sync extraction / hook / bridge / storage schema、出荷 zip allowlist を順番に確認します。verify-package は最後に実行され、dist/ に検証用 zip を書き出します。
実行順を確認したい場合:
node scripts/check-all.mjs --list個別に確認する場合は次を実行します。
node tests/scripts/verify-phase1-static.mjs
node tests/scripts/verify-docs-consistency.mjs
node tests/scripts/audit-operational-alignment.mjs
node tests/scripts/verify-f1a-observation-safety.mjs
node tests/scripts/verify-f1a-main-hook-simulator.mjs
node tests/scripts/verify-sync-extraction.mjs
node tests/scripts/verify-sync-hook.mjs
node tests/scripts/verify-sync-bridge.mjs
node tests/scripts/verify-storage-sync-schema.mjs
node tests/scripts/verify-package.mjsevaluate-f1-observation.mjs は --live を付けた場合だけ、条件充足時に f1a_viable を返します。--live なしでは fixture 扱いのため、条件が揃っても fixture_pass です。
実 X の masked summary を評価する場合:
node tests/scripts/evaluate-f1-observation.mjs --live path\to\masked-summary.jsonunsafe_summary が出た場合は raw 値が混入している可能性があるため、その summary は共有せず削除します。f1a_insufficient の場合は F1-A primary に進まず、F1-B または F1-D fallback を検討します。
docs/manual-popup-verification.mddocs/local-chrome-synthetic-verification.mddocs/phase2-readiness-gates.mddocs/privacy-threat-model.mddocs/deferred-findings-register.mddocs/research/f1-a-main-world-hook.mddocs/decisions/f1-source-selection.md
運用ルールは現行のユーザー指示、AGENTS.md の不変条件、CODEX_HANDOFF.md の現状メモを参照します。2026-06-13 以降の要点:
- 報告は日本語、冒頭に日本時間
YYYY/MM/DD HH:MM:SS。テスト結果・commit hash・URL を捏造しない。 - 現行ユーザー指示と人間承認ゲートに従い、通常の docs・test・code 健全性作業は自走する。旧 ChatGPT 承認制は廃止。
- ユーザー同意の下、Claude Code は Chrome MCP でログイン済み Chrome を操作し、設定ページ限定で masked observation を収集してよい。password / MFA / Cookie / token は受け取らない。x.com / twitter.com タブではスクリーンショット・DOM テキスト・network response を読み取らない。
- Chrome Load unpacked / popup / synthetic fixture の確認は Playwright/CDP 自動化で実施してよい。
- 入力待ちループ、対話式 CLI 待機、foreground dev server で待機しない。検証スクリプトは必ず終了する。
- 権限は
storage+ x.com/twitter.com host に保つ。scriptingは M7 で retire 済みです。追加が必要なら理由・脅威モデル更新・rollback をユーザー承認と共に docs に残す。
2026-06-13 以降、ユーザー承認の下で Claude Code が自動検証(Chrome Load unpacked は Playwright/CDP、live X は Chrome MCP の masked observation)を実施しました。
- Chrome の
Load unpacked/ popup 動作 / synthetic + 実DOM フィルタはtests/scripts/verify-extension-load-chrome.mjs(実 Chromium CDP)で自動検証済み(M2 / M5)。2026-07-28 に options page の390x844 / 768x1024 / 1280x900responsive layout、主要 text / control、Runtime / console error、failed request の bounded 検証を追加した。watchdog も通常終了と同じ冪等 cleanup を通り、発火後の exit 0 を禁止する。Browser.close/taskkill/ child exit / 一時 profile の失敗は集約して非 0 終了とする。2026-07-29 に、Browser.close=ok・直接 child 終了・profile 削除・helper 正常終了を確認済みの既知 no-process exit 128 だけを benign とする cleanup policy を追加し、unknown nonzero / timeout / helper 異常は fail-closed のまま固定した。 - production sync hook は2026-07-29のPR #53(merge
49a7c61)で、同一documentでのscript再評価後も初回APIのteardown所有権を保ち、再install後の本文読取/messageを各1回に固定した。exact merge commitでhook 101件と静的10本をPASS。GitHub check rollupとbranch workflow runは各0件でremote CI証跡はない。 - 同日の bounded review では、外部
openwrapper内の同期DONEに対し、次request stateをdelegate中に公開せず、originalOpen正常復帰後だけ有効化する境界を追加した。委譲後・return前の同期DONEは正常復帰後に1回だけ再確認し、DONE後にwrapperが同期throwした場合は本文・messageを0件に保つ。install世代共有のper-XHR coordinatorはactive/inactive wrapperのdelegation depthをtry/finallyで追跡し、depth > 0中のnested open treeを全世代でambiguous化してinner / outerのcommit・即時処理を禁止する。tree unwind後の独立top-level openだけ再armするため、曖昧なtree内の有効responseを落とし得るavailability tradeoffを安全側に採用した。commit後のinactive旧wrapper直呼び、同一inactive wrapperの二重通過、retained inactive ancestor配下のactive nested openもfail closedにする。synthetic hook 138件をPASSし、live X・raw response・権限・storage schemaは変更していない。 - 上記follow-upはPR #55(head
a7e6a2a、merge596c4e2)でmainへ統合した。exact merge commitで静的10本をPASSし、PR checkとhead commitのworkflow runは各0件だった。main merge commitのGitHub Pages build/deploy run30429121391はsuccess、Pages statusはbuilt、公開URLのHEADはHTTP 200だが、これはコードテストCIの代替ではない。 - 次の bounded review では、公開installed flagのfalse driftを同じAPIの再installとscript再評価の両方でsynthetic再現した。private active ownershipを正本にして既存API / wrapper identityとnative teardown所有権を保ち、公開flagはmirrorとして自己修復する。private stateなしで公開flagだけtrueの場合はfresh installを妨げない。targeted hook 148件と静的10本をPASSし、PR #57(head
05d80d8、merge7407fbf)でmainへ統合した。exact merge commitの静的10本もPASSし、PR check rollupは0件だった。 - 実 X 画面での F1-A endpoint / response shape / pagination / identity は M3 の live masked summary 評価で
f1a_viableを確認済み。production sync は実アカウントで件数のみ確認済み(M4、blocked 234 / muted 50)。 - 実 X DOM の投稿者判定は User-Name 領域限定 + quote / embed 分離で実装・確認済み(M5)。同期の主キー
user_id(rest_id)と補助キー handle(screen_name)は一覧 GraphQL 応答から取得する。 - Chrome Web Store 提出(M7)は審査を通過し公開済み(v1.1.1、オーナー確認 2026-07-06)。
プライバシーポリシーは docs/privacy-policy.md(日英併記)と、ホスティング用の自己完結
HTML docs/privacy-policy.html にあります。連絡先と想定公開 URL のプレースホルダは 2026-06-14 に解消し、文書は公開済みです。連絡先値と URL 値は README に転記しません。
今回の文書同期では、公開 URL の現在の疎通と Chrome Web Store 管理画面の設定値は未確認です。今後の文書更新ではポリシー本文、公開 URL、ストア掲載情報の整合をオーナーが確認し、ストアの更新・再提出・公開は人間承認ゲートとして維持します。
出荷用 zip は allowlist 方式で、拡張に必要なファイルだけを同梱します(research のオフライン証跡・tests・docs・scripts・*.md・icons/icon.svg は除外)。
node scripts/build-package.mjs # dist/TrueBlock-Mute-v<version>.zip を生成
node tests/scripts/verify-package.mjs # allowlist 完全性・禁止パス不在・ZIP 妥当性を検証dist/ は gitignore 済みです。アイコンは node scripts/make-icons.mjs で icons/icon.svg から再生成できます。
このプロジェクトは X Corp.、Twitter、または Chrome Web Store レビュアーと提携、承認、公式接続されたものではありません。