Skip to content

Repository files navigation

x-true-block-mute

Current status

この 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|MutedAccounts pathname に厳密限定し、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)を維持します。

Phase 0 の範囲

  • Chrome Manifest V3 の manifest.json を作成する
  • 対象予定ドメインとして https://x.com/*https://twitter.com/*host_permissions を宣言する
  • ローカル読み込み手順と開発上の注意を文書化する

Phase 0 で実装しないこと

  • DOM フィルタ
  • ストレージ層
  • popup の表示や操作
  • content script
  • background service worker
  • F1-A、F1-B、F1-C、F1-D の取得処理
  • webRequestcookiestabsactiveTab<all_urls>https://api.x.com/* などの追加権限

Phase 1 の範囲

  • 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 が登録対象に一致したカードを処理する
  • 表示モードとして hiddenplaceholderoff を扱う
  • tests/fixtures/home-timeline.html でログイン不要の synthetic 確認を行う

Phase 1 で実装しないこと

  • F1-A / F1-B / F1-C / F1-D の一覧取得処理
  • X API 連携、OAuth、Cookie、CSRF token、実アカウントデータ取得
  • 本番用の MAIN world fetch / XMLHttpRequest hook
  • webRequestcookiestabsactiveTab<all_urls>https://api.x.com/*
  • import/export、options page

Phase 1 の real DOM 制限

現在の 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 著者判定ロジックを変更しません。

Phase 1.5 の範囲(履歴)

この節は F1-A feasibility investigation 当時の履歴です。現行 v1.1 / M7 の出荷版では research UI と動的注入を retire し、manifest の permissionsstorage のみです。本番同期は manifest の宣言的 world:"MAIN" content script で行うため、scripting 権限は使いません。

  • F1-A feasibility investigation のため、/settings/blocked/all/settings/muted/all に限定した研究用 bridge を追加する
  • 当時は chrome.scripting.executeScriptworld: "MAIN"fetch / XMLHttpRequest hook を入れる
  • 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 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 に読み込む手順

  1. Chrome で chrome://extensions を開く。
  2. 右上の Developer mode を有効にする。
  3. Load unpacked をクリックする。
  4. この拡張のフォルダ(manifest.json がある場所)を選択する。
  5. x-true-block-mute が表示され、manifest エラーが出ていないことを確認する。
  6. 拡張アイコンの popup を開き、通常フィルタローカル確認用データブロック・ミュート同期 が表示されることを確認する(F1-A 観測メモ(開発用) は本番では非表示。開発時に確認する場合は src/shared/constants.jsRESEARCH_UI_ENABLEDtrue にして拡張を再読み込みする)。
  7. 初心者向けの確認は docs/manual-popup-verification.md の手順に沿って行う。

設定ページ(オプション)

popup の 詳細設定・プライバシー から設定ページ(src/options/options.htmloptions_ui で登録)を開けます。設定ページでは次を確認・操作できます。

  • プライバシー説明: データは端末内(chrome.storage.local)のみに保存され外部送信なし、取り込むのは user_id・handle・listKind のみ、権限は storage と x.com / twitter.com host のみ。
  • フィルタ対象の一覧(透明性): 同期で取り込んだブロック / ミュート対象の件数と一覧、ローカル確認用データの件数。
  • 管理: 同期データの削除、テストデータの削除。
  • 「うまく同期できないとき」のトラブルシュート手順(P2-015 のエラー日本語ガイダンス)。

現在の manifest 権限

宣言している権限は次だけです。

  • permissions

    • storage
  • host_permissions

    • https://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)はリポジトリに残しますが、出荷パッケージには含めません。

webRequestcookiestabsactiveTab<all_urls>https://api.x.com/*scripting は宣言していません。

Storage schema

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: booleanxtbmBaseEntriesxtbmLegacySyntheticEntries、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 外

Synthetic fixture での手動確認

X にログインせず、実アカウントの投稿内容や Cookie を読まずに確認できます。

  1. 拡張 popup を開き、ローカル確認用データテストデータを入れる を押す。
  2. 登録済みの対象0件 以外になったことを確認する。
  3. tests/fixtures/home-timeline.html をブラウザで開く。
  4. 説明だけ表示 を押す。
  5. 非対象の投稿だけ本文が残り、user_id 対象と handle-only 対象の投稿本文が中立プレースホルダに置き換わることを確認する。
  6. 完全に隠す を押す。
  7. 対象投稿カードが表示領域から消え、非対象投稿が残ることを確認する。
  8. 何もしない を押す。
  9. 置換済みカードが復元され、対象投稿本文が再表示されることを確認する。
  10. popup の テストデータを消す を押す。
  11. 対象投稿が処理されない状態に戻ることを確認する。

popup で見る場所、件数の意味、貼ってよい情報、貼ってはいけない情報は docs/manual-popup-verification.md にまとめています。

Static validation

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.mjs

evaluate-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.json

unsafe_summary が出た場合は raw 値が混入している可能性があるため、その summary は共有せず削除します。f1a_insufficient の場合は F1-A primary に進まず、F1-B または F1-D fallback を検討します。

Phase 1.5 research docs

  • docs/manual-popup-verification.md
  • docs/local-chrome-synthetic-verification.md
  • docs/phase2-readiness-gates.md
  • docs/privacy-threat-model.md
  • docs/deferred-findings-register.md
  • docs/research/f1-a-main-world-hook.md
  • docs/decisions/f1-source-selection.md

Claude Code operation notes

運用ルールは現行のユーザー指示、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 / 1280x900 responsive 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 では、外部 open wrapper内の同期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、merge 596c4e2)でmainへ統合した。exact merge commitで静的10本をPASSし、PR checkとhead commitのworkflow runは各0件だった。main merge commitのGitHub Pages build/deploy run 30429121391 は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、merge 7407fbf)で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、ストア掲載情報の整合をオーナーが確認し、ストアの更新・再提出・公開は人間承認ゲートとして維持します。

パッケージング(Chrome Web Store 用)

出荷用 zip は allowlist 方式で、拡張に必要なファイルだけを同梱します(research のオフライン証跡・tests・docs・scripts・*.mdicons/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.mjsicons/icon.svg から再生成できます。

関係性の表明

このプロジェクトは X Corp.、Twitter、または Chrome Web Store レビュアーと提携、承認、公式接続されたものではありません。

About

Local Codex project

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages