Skip to content

[deps] フォーク依存の棚卸し:zed / gpui-kit / cosmic-text / gpui-terminal は何を直しているのか、どれを捨てられるのか #1479

Open

Description

@TomiXRM

現在の追跡先・受入条件(v0.45.0後のIssue整理)

#436 の依存移行は本Issueの S6 に統合し、#466 の旧中間revision案は採用しない。以下の元本文は2026-10-13時点の調査記録として保存する。現行方針は後続のオーナー承認と upstream-migration-1479.md に従い、公式Kit revision+registry gpui-pre、fork/patch追加なし。v0.45はS0〜S5の準備までであり、本Issue/S6は未完了。

#436から引き継ぐS6受入(元Issueを閉じても未完了のまま維持)

  • root・各UI crate・web・terminalを含む全manifestが単一GPUI供給元で、active dependency closureに重複GPUI/fork source/patchがない。component/base/assetsは同一の採用公式revisionに揃える。
  • 旧InputStateの複数行/コード用途をTextarea/Editorへ移行し、公開Find/Radio/Select/theme APIを使用。日本語コミット入力・IMEを実機で確認し、人間の受入結果を記録する。
  • gpui_kit_assetsへの移行と実際のicon描画を確認。
  • Dark/LightのThemeColor/tokens同期とsyntax highlightingが崩れないことを実画面で確認。
  • build/workspace testsと関連nativeを通す。kagi-web buildと既存Playwright 2 spec、移行計画にあるLinux/Windows/WASM/AX等のgateを省略しない。
  • ADR-0001/0006等に公式供給元・revisionを選んだ根拠とgpui-pre採用判断を残す。古いrev-less/リポジトリ名/patchコメントを更新し、research auditの旧0.5.1前提を明示または更新。
  • Markdown固有の移行・画像・table・Mermaid・選択copy・sanitizer検証は [deps][Markdown] 公式GPUI Kit移行時の描画・選択・sanitizer受入を保持する(#466後続) #1634 の全checklistと合わせて受け入れる。#466のGPLゲートと製品要件は失効しない。

関連するcapture lifecycleの残検証は #1632。#436/#466のcloseは重複/旧計画の整理であり、S6完了やv0.45での公式依存切替を意味しない。


1. 目的

Kagi の依存関係には、自前のフォーク(独自の修正を当てた複製)が4つあります。これらの修正は主に AI が書いたもので、人間はその中身を把握できていません。フォークが増えるほど本家(upstream)の更新に追従できなくなるため、なるべく減らしたいと考えています。

この Issue では、各フォークについて次の4点を一次資料(git の差分、upstream のコード、Kagi 内の呼び出し箇所)から確かめた結果をまとめます。

  1. 何の問題を直しているのか
  2. Kagi のどこがそれに依存しているのか
  3. upstream ではすでに直っているのか
  4. 使い方を変えればフォーク自体が要らないのではないか、upstream に PR を出せば取り込まれそうか

調査日は 2026-10-13 です。この調査では、Kagi 本体のコードを一切変更していません。

2. 結論(先に要点)

フォーク 中身 捨てられるか 次の一手
TomiXRM/cosmic-text 1コミット。ja-JP のような地域付きのロケールを判定できず、漢字が中国語(簡体字)の字形で描かれる不具合を直す ほぼ捨てられる(Linux のみの問題で、Kagi 側の設定で回避できる) 端末の描画にフォールバックフォントを明示してから patch を削除する
TomiXRM/zed 1コミット、gpui/src/elements/list.rs だけ。可変高リストで、未計測の行の推定高さを保持する。末尾までスクロールしたい意図も扱う 今は捨てられない upstream に2つに分けて提案する。ADR-0222 の事実誤認を直す
TomiXRM/gpui-kit(gpui-component) 12コミット・+4361/−330 行。2026-10-10〜13 の4日間で積まれた 今は捨てられない。半分ほどは upstream の最新版ですでに解決済み #436 の移行を最優先する。小さな修正3件は upstream に PR を出す
vendor/gpui-terminal 選択とコピー、端末からの問い合わせへの応答、マウス報告、貼り付けの安全化など。実質的には Kagi が自分で持っている端末部品 捨てられない(upstream はほぼ停止している) 「一時的なフォーク」という扱いをやめ、Kagi のコードとして文書化する

いちばん大きな問題は個々のパッチではありません。進め方そのものです。

AI に1件ずつ課題を直させると、「upstream に合わせる」より「手元のフォークに足す」ほうが毎回安く見えるため、フォークは膨らみ続けます。これは方針として止める必要があります(§7 の提案)。


3. cosmic-text フォーク

宣言箇所:Cargo.toml の [patch.crates-io] cosmic-text = { git = "https://github.com/TomiXRM/cosmic-text", branch = "fix-han-unification-0.19" }(コミット 7c9c36a8)

何を直しているか

  • upstream のタグ 0.19.0 に1コミットを足したものです。対象は src/font/fallback/unix.rs の han_unification() で、+37/−5 行とテストの追加です。
  • 不具合:cosmic-text は OS のロケールを "ja-JP" の形で受け取ります。ところが判定は "ja" に完全一致するかどうかだけでした。そのため日本語や韓国語の環境が、すべて既定の「Noto Sans CJK SC(簡体字)」に流れていました。結果として、かなは日本語の字形、漢字は中国語の字形という混在が起きます。
  • 修正内容は、ロケールを - _ . @ で区切り、言語の部分(と中国語の地域)で判定することです。

upstream の状況

実は Linux でしか効いていない

  • cosmic-text は gpui_wgpu 経由でしか使われず、gpui_wgpu を使うのは Linux/FreeBSD だけです。macOS は CoreText、Windows は DirectWrite を使います。macOS 版にとって、このパッチは何もしていません。

使い方の問題ではないか → 大部分はそうです

  • ADR-0130 で Noto Sans JP を同梱し、crates/kagi-ui-core/src/fonts.rs の ui_font() が FontFallbacks(["Noto Sans JP"]) を Root に設定しています。そのため、通常の UI やコード表示は cosmic の自動判定(不具合のある経路)にそもそも到達しません。
  • 残っている経路は端末です。vendor/gpui-terminal/src/render.rs:247 と :633 が fallbacks: None のフォントを作っているため、端末に表示する日本語だけが不具合のある経路を通ります(この2行は確認済み)。
  • さらに、パッチがあっても cosmic が選ぶのは OS に入っている「Noto Sans CJK JP」です。最小構成の環境や AppImage など、そのフォントが入っていない環境では、パッチは何の役にも立ちません。

提案

  1. TerminalConfig にフォールバックフォントの項目を足し、render.rs の2か所に Noto Sans JP を渡す。
  2. Ubuntu の ja_JP 環境(fonts-noto-cjk あり)で、端末・差分・コード表示の「直・骨・誤」が日本語の字形になることを確かめる。
  3. 確かめられたら [patch.crates-io] cosmic-text を削除する。PR Backend::run: CreateBranchWithCheckout の途中失敗が oplog 記録を迂回する #522 は放置せず、催促するか取り下げるかを決める。

確度:中〜高です。端末以外に「フォールバック指定なしで文字を描く経路」が残っていないかは、網羅的には調べていません。


4. zed(gpui)フォーク

宣言箇所:Cargo.toml の [patch."https://github.com/zed-industries/zed"](22 crate すべてを TomiXRM/zed@caf50076 に向けている)。関連文書:ADR-0222、docs/decisions.md の 2026-10-10 の行

何を直しているか

  • upstream の 90b3aa0(2026-07-13)に1コミットを足したものです。変更は crates/gpui/src/elements/list.rs の1ファイルだけで、+380/−42 行(うち約260行はテスト)です。
  • 他の21 crate は中身が upstream と同一です。 取得元を揃えるためだけに fork に向けています。
  • 新しい公開 API はありません。 upstream にすでにある ListState::with_uniform_item_height と reset_with_uniform_height の振る舞いを変えています。
    1. 推定の高さが、splice や reset、幅の変更で失われないようにする。upstream では一度設定すると、これらの操作で消えます。
    2. 同じ値で再び呼んだときは何もしない。upstream では毎回全行を作り直し、計測結果がすべて捨てられます。
    3. 推定した末尾まで一気にスクロールしたら、本当の最後の行に揃える(end intent)。
    4. 幅が変わってもスクロール位置を保つ。

ADR の事実誤認

  • ADR-0222 と decisions.md の 2026-10-10 の行は「upstream の list は estimated-height API を持たない」と書いています。これは誤りです。 基準コミット 90b3aa0 には with_uniform_item_height、reset_with_uniform_height、measure_all がすでにあります(確認済み)。
  • 正しくは「upstream にも推定高さの指定はあるが、splice、reset、幅の変更で失われる」です。この誤りは AI が書いた ADR をそのまま採用したことで生まれています。

依存箇所

  • crates/kagi-ui-core/src/diff_list.rs:58 の DiffListLayout::sync は、描画のたびに with_uniform_item_height を呼びます。upstream の振る舞いのままだと毎回全計測が捨てられるので、このコードはフォークの「同じ値なら何もしない」に暗黙に頼っています。
  • ADR は「diff list だけが opt-in する」と書いていますが、これも実態と違います。with_uniform_item_height を呼ぶ他のリストもすべて新しい挙動になっています。該当箇所は src/ui/pr_lane.rs:237、issue_conversation.rs:105、tab_view.rs:601、issues_mode/feed.rs:181、inspector_files.rs:140、operations/oplog_restore_inspection.rs:82 です。

upstream の状況・代替案

  • zed main(cb13b37、2026-10-13)にも同等の修正はありません。TomiXRM からの PR もありません。
  • 代替案1:ListState::measure_all()。 最初のフレームで全行を計測します。スクロールバーと末尾が正確になり、フォークは不要になります。代わりに、差分の全行で文字の配置計算が走ります。行数が数千程度なら許容範囲と思われますが、1万行を超えると1回の計測に数百ミリ秒〜数秒かかると見積もっています(実測はしていません)。「行数が少なければ measure_all、多ければ別の方法」という組み合わせは検討に値します。
  • 代替案2:scroll_to_end() と is_scrolled_to_end() を使い、末尾に揃える処理を Kagi 側でまねる。ただし推定高さが失われる問題は残ります。

提案

  1. 今は維持します(確度:高)。外すと [HIG][Diff] 大規模diffで未測定行がheight=0扱いになり、scrollbarが嘘をつき末尾へ到達できない #1122 の退行が戻ります。
  2. upstream には2つに分けて提案します。(a)「推定高さが splice/reset/幅変更でも保たれる・同じ値の再設定では何もしない」は既存 API の不具合修正として出しやすいはずです。(b) end intent はスクロールの意味を変えるので、議論が必要です。zed の CONTRIBUTING は「機能追加はコードを書く前に議論を」と求めているので、まず Discussion を立てます。取り込まれる見込みは、分割すれば五分五分程度です(低確度の推測)。
  3. ADR-0222 と decisions.md の誤りを訂正します。
  4. フォークの差分は list.rs だけで、upstream 側の list.rs の変更とも重ならないため、rebase のコストは低いです。

5. gpui-kit(gpui-component)フォーク

宣言箇所:Cargo.toml の [patch."https://github.com/longbridge/gpui-component"](TomiXRM/gpui-kit@ee625584、fork 側の branch は kagi/undo-group)

  • 基準は b004e595(2026-07-13)で、その上に12コミットを直線的に積んでいます。すべて 2026-10-10〜13 のものです。
  • upstream main(8d4907d、2026-10-13)はそこから 628 コミット先です。crate 構成は base / component / kit に分かれ、GPUI の取得元は crates.io の gpui-pre =0.3.9 に変わっています(確認済み)。
  • Cargo.toml:174 付近のコメント「gpui-component の fork patches は dropped」は現状と食い違っており、読む人を誤らせます。
コミット 直している問題 Kagi の依存箇所 upstream main の状況 判断
941f20e6(+3.4k 行), ae37bfd4 仮想リストで画面外に外れた投稿も含めて、まとめて選択・コピーできるようにする(Root::set_text_selection_group など) src/ui/issue_conversation.rs、src/ui/issues_thread.rs(ADR-0198) 一部だけ upstream に入りました(#3421、マージ済み)。ただし #3421 自身が「外側の仮想リストで外れた行は扱わない」と明記しています。upstream には新しい選択の仕組みがありますが、これで足りるかは未検証です 当面は維持。最も重い部分です。upstream の新しい仕組みの上で作り直すか、機能そのものを見直す必要があります
f7e696f6 InputState::wrap_row_count():折り返し後の行数を公開する src/ui/commit_panel_render.rs:405 公開されていません upstream に PR(小さく汎用的)
b50a0796 入力欄が次フレームの処理で自分自身を強く保持し、メモリが解放されない 挙動のみ(pr_detail_lifecycle のリーク検査) 解決済み(仕組みごと作り直されています) 移行すれば不要
13386100, 879f3bfa ラジオボタンの選択表示がチェックマークになっていたのを、中央の丸に変える src/ui/settings_view.rs(見た目のみ) 直っていません upstream に PR。見た目の好みと見なされて断られる可能性はあります
3dad9fcc, ac2f8ceb, f5f68275 の一部 検索バーのボタンに読み上げ名とツールチップがない。日本語化できない。Tab や Enter が裏のエディタに漏れる crates/kagi-ui-core/src/find.rs、kagi-ui-editor、conflict_view.rs、issues_composer.rs 直っていません(upstream には日本語訳もありません) upstream に PR(汎用的な部分)。テスト用の覗き見用フックはフォークに残すか、設計し直す
406b02f2 Select の読み上げ情報を、実際にフォーカスを持つ部品に付ける src/ui/settings_view.rs:487,501-504 別の API で解決済み(accessibility_label など) 移行時に呼び出し名を変えれば不要
a97efac0, f5f68275 Input::aria_label:入力欄に読み上げ名を付ける 約18か所 解決済み(Input::aria_label が upstream にあります。確認済み) 移行すれば不要。テスト専用の補助関数は代わりが必要
ee625584 文面の全置換(Smart 生成)が直前の入力と同じ取り消し単位にまとまり、元に戻すと手入力まで消える src/ui/commit_panel_composer.rs ほか 解決済み(取り消し管理が作り直されています) 移行すれば不要
(Cargo.toml) test-support feature gui-e2e feature upstream にあります 移行すれば不要

まとめ

  • 12コミットのうち、約半分は upstream main ですでに解決済みです。これらのフォークは「古い基準に留まっているから必要になっている」だけです。
  • 本当に upstream に無いものは4つです。仮想リストを横断する選択、wrap_row_count、ラジオの丸、検索バーの読み上げです。
  • b004e595 に戻してフォークを外すことはできません(どのコミット群にも Kagi のコードが依存しています)。
  • フォークを減らす現実的な道は、[deps] gpui-component v0.6.0 / GPUI Kit 改称への追従(gpui-pre 0.3.x への供給元切り替えを含む) #436(upstream main への移行)を進めることだけです。移行すると GPUI の取得元が crates.io の gpui-pre に変わるため、§4 の zed 側の修正は [patch.crates-io] の gpui-pre* 向けに移し替える必要があります。

6. vendor/gpui-terminal

宣言箇所:Cargo.toml:51 の path 依存(ADR-0035)。元は crates.io の gpui-terminal 0.1.0(zortax/gpui-terminal)

Kagi の変更内容

crates.io 版との差分で確認しました。

  1. マウスでの選択と Cmd+C でのコピー(ADR-0035 が挙げている理由)
  2. 端末からの問い合わせへの応答(PtyWrite)。zellij が起動時に止まる問題を直します。process_bytes の戻り値が変わるAPI 変更です
  3. マウス操作を端末アプリに伝える機能(SGR mouse reporting)と、スクロールの振り分け
  4. Cmd を押しながらのキー入力で文字が漏れる問題の修正
  5. 貼り付けの安全化(改行や制御文字を含む貼り付けの確認)
  6. 新しい gpui への追従(crates.io の gpui 0.2.2 から zed の git 版へ)
  7. upstream にあったテストの一部を削除

upstream の状況

  • リリースは 0.1.0(2025-12)のみで、最終コミットは 2026-01-11 です。他の人の PR も止まっています。実質的に止まっています。
  • crates.io の gpui を前提にしているため、Kagi の変更をそのまま返すこともできません。

判断

  • ADR-0035 が言う「upstream が選択とコピーを実装したら外す」という条件は、満たされる見込みがありません。
  • これはフォークではなく Kagi のコードです。 ADR-0035 を「一時的な vendor」から「Kagi が所有する端末部品」へ改め、保守の責任を明確にすべきです。
  • Zed の terminal crate は GPL なので、代わりには使えません(ADR-0008)。alacritty_terminal を直接使う案は、今の状態とほぼ同じ作業量になります。

7. 提案(優先順)

  1. [方針] フォークへの新規コミットを原則止める。 gpui-kit や zed のフォークに足すときは、同時に upstream への PR(または Discussion)を出すことを条件にします。出せない理由があれば docs/decisions.md に書きます。これを CLAUDE.md の不変条件に加え、AI が「フォークに足すほうが安い」を選び続ける構造を断ちます。
  2. [文書] Cargo.toml の patch ごとに、「何を直しているか・upstream の PR/Issue・外す条件」のコメントを付ける。 誤っている「gpui-component fork patches were dropped」は削除します。ADR-0222 の「upstream に API が無い」を訂正します。
  3. [すぐ外せる] cosmic-text:§3 の手順で端末にフォールバックを足し、patch を削除します。
  4. [upstream PR・小] gpui-kit 向けに wrap_row_count、ラジオの丸、検索バーの読み上げ。ただし upstream main(新しい crate 構成)に向けて出す必要があります。b004 基準のコードはそのままでは当たりません。
  5. [upstream 提案] zed の list 修正を2つに分けて Discussion を立てます。
  6. [本丸] [deps] gpui-component v0.6.0 / GPUI Kit 改称への追従(gpui-pre 0.3.x への供給元切り替えを含む) #436 を再開します。 これ以上 b004 の上に積むと、移行のコストは毎日増えます。移行で gpui-kit フォークの約半分が消えます。
  7. [文書] gpui-terminal:ADR-0035 を「Kagi 所有」に改めます。

8. 未確認・判断できなかった点

  • upstream gpui-kit の新しい選択の仕組みで、仮想リストの画面外の行のコピーを実現できるか(#3421 の記述からは「できない」と読めますが、コードでは確かめていません)。
  • measure_all を Kagi の大きな差分に使ったときの実際の速度(未計測で、上の数字は推定です)。
  • 端末以外に、フォールバック指定なしで日本語を描く経路が残っていないか(網羅していません)。
  • ラジオの丸の変更が upstream に「不具合修正」と認められるか。
  • 検索バーのキーボード操作のうち、upstream の cycle_focus でどこまで足りるか。

9. 一次資料

Activity

  1. TomiXRM commented on Oct 14, 2026

    @TomiXRM
    OwnerAuthor

    #1479 フォーク依存の再検証と #436 移行判断

    調査基準日: 2026-10-15。調査のみ。Kagi 本体・worktree の編集、commit、push、PR、Issue コメントは行っていない。Kagi は origin/dev = 91d2d10570cd88fc8e43151c8bb0a63003bb2a4a を /tmp/kagi-fork-review/kagi に読み取り用に固定し、GitHub の dev tip と一致することも確認した。以下の Kagi:行 はこの snapshot の行番号であり、#1479 に記載された古い行番号とは異なる。

    一次資料: #1479、#436、Kagi 基準、GPUI Kit main 基準。GitHub の旧 URL longbridge/gpui-component は longbridge/gpui-kit への redirect。

    本報告の「可能」は、依存解決の実験で確認した事項と、公開 API を読んで成立すると判断した設計を分けている。Kagi 全体を移行して build/E2E を完了した意味ではない。コード片は置換設計の sketch であり、未実装の Kagi 機能を実装済みとは扱わない。

    結論表

    対象 現在の独自差分 公式 upstream のままでの到達点 推奨判断
    cosmic-text 7c9c36a8、1 commit、+43/−6 同梱JP fallbackをterminalのglyph/metricsとstatus幅計測に明示し、描画外test計測も揃えればlocale patchに頼らない設計へ。公式bug自体は未修正 撤去候補。端末2行だけでは足りない。Linux字形確認をgateにする
    Zed/GPUI caf50076、1 file、+380/−42、22 sibling packageをpatch 小規模measure-allは公開APIで可能。大規模のextent/drag/splice/resize契約はKagi-owned geometry/virtual listが必要 forkのgpui-pre移植ではなくアプリ側cutover。単純なmeasure-all/End二方式だけでは不可
    GPUI Kit 7a116a20まで13 commits、34 files、最終+4384/−338。upstream645 commits先 AX/Undo/test API等は公式済み。Radio/Find/Clear/IMEは公開primitive/engine/traitでKagi側へ。outer virtual-listのper-post選択は公式TextView as-isでは不足 公式mainへ移行。ただし単なる版上げで同等にならない。選択renderer所有又は上流extensionが最大の条件
    gpui-terminal release0.1.0由来。Kagi scoped13 commits(import含む) official gpui-terminal as-isは機能/GPUI型が不足。official alacritty_terminal+portable-pty上のKagi adapterとして保守できる 責任境界をKagi-ownedに明示し既存コードを活用。派生物/ライセンス義務は消えない

    zero_patch_feasible = true の意味: dependency解決自体は実験で確認済み。製品側に不足機能を正しく所有させる設計なら全[patch]を除去できる。ただし 現行コードのpatch行だけを削除して使える、すべての外部UI widgetをそのまま残せる、Kagi移行build/E2Eに合格した、という意味ではない。

    最も重要な制約は、現行の完全な投稿選択契約・公式TextView無改造・独立した外側投稿virtualizationを全部維持する組合せには公開extension seamが足りないこと。その組合せを必須にするならupstream変更がblockerになる。zero-patch案は、単なるwrapperではなくKagi-owned rich renderer/document adapterへ責務を移す(又は上流seamを得る)ことを明示的に含む。cross-post選択を黙って落とす案ではない。

    #1479 のclaim再判定一覧

    「確認」は現在の一次資料と一致、「変更」は当時のsnapshotから変化、「誤り」は現在のコードで成立しない、数値的な採用確率等は「根拠なし」とする。詳細なcommit/API/行番号は後続節。

    #1479のclaim 再判定 現在の事実
    対象forkはcosmic/Zed/Kit/vendor terminalの4系統 確認 ただしvendorを改名しても派生物であることは変わらない
    cosmicは0.19.0上の1commit、unix.rsのみ 確認 fork7c9c36a8、親c24886c2
    cosmic差分+37/−5 誤り commit全体はtests込み+43/−6
    ja-JP/ko-KR完全一致問題とSC落ち、-_.@分割 確認 分割は最初の2subtag、一般BCP47正規化ではない
    cosmic公式main未修正、release0.19.0、PR522 open/レビューなし 確認 今回reviews[]/comments[]も確認
    cosmic/gpui_wgpuはLinux/FreeBSDだけ 誤り gpui_webもCosmicTextSystemを利用。ただしunix.rs patchはWASMには効かない
    macOSにはこのpatchは効かない、WindowsはDirectWrite 確認 native CoreText/DirectWriteで区別
    通常native UI/codeはRootで明示Noto fallback 条件付き確認 familyだけ変更なら維持。fresh Font直接shaping/描画外styleは別。primaryにglyphがあればprimary優先
    残りのno-fallback本番経路は端末だけ 誤り status_text_widthがfresh Font。test metricsとRoot無しWASM catalogも別途あり
    terminal247/633のfallbacks None 確認 247はASCII Mのセル計測、633が実glyph。両方統一
    OS Noto Sans CJK JPが無ければpatchは無意味 趣旨確認・断定過剰 同梱Noto Sans JPを使う決定性を保証しない。他のfallback探索は存在する
    Zedは基準90b3aa0上1commit/list.rsのみ/22packages同source 確認 +380/−42、他21 crate差分なし、新public APIなし
    upstreamにestimated-height APIは無いというADRは誤り 確認 with_uniform/reset_with_uniform/measure_allは基準にも既存
    reset/splice/widthでestimate消失、同値設定でMeasured破棄 確認・補足 初回layoutでも消失。既知サイズhintまで全消去ではない
    forkがあらゆる推定末尾操作を実tailへ合わせる 誤り wheel/scroll_byが対象、Normal scrollbar drag自体には補正無し
    幅変更でもfractional anchor 誤り/要区別 自動width枝はabsolute。明示remeasure/estimate変更の比例anchorと別
    diffの毎render同値設定がfork依存、diff以外6 callersも対象 確認 diff_list.rs現在71行、各callsiteは後述
    Zed mainにも同等fix無し、TomiXRM PR無し 確認・head変更 main c403bc6a。公式pre0.3.9 list.rsと同一
    measure-allは数千なら許容、1万超は数百ms〜数秒 実測に更新 モデル上1k17.656/5k87.998/20k399.904ms。数秒は今回観測していない
    小measure-all+大scroll_to_end/is_scrolled_to_endで代替候補 そのままでは不足 unknown heightで末尾判定None、offscreen spliceの再armとextent/dragが未解決
    upstream PR分割なら採用可能性五分五分、rebase安価 根拠なし/限定 API差分は読めるが、採用確率・将来保守費は測定できない
    Kit12commits/ee625584/+4361−330/4日間 変更 13commits/7a116a20/+4384−338。作者JST10/10〜14
    Kit upstream628 ahead/8d4907d 変更 645 ahead/1188c110
    crate split base/component/kit、gpui-pre=0.3.9 確認 published0.7.3とmainのAPIは別snapshot
    TomiXRM upstream PRは#3421の1件、merged 確認 presentation retentionの一部、全fork取り込みではない
    Cargoコメントが『全component forkはdropped』と言う 誤った読み方 実際は旧cosmetic2種を限定。ただし再追加を勧める旧コメントは現方針に不適切
    wrap_row_countは公式に無い 変更 Editorのpublic display_map().wrap_row_countあり。Textarea直接getter無し、auto_growあり
    next-frame retain leak解決 対象経路は確認 当該strong closure無し。全lifecycle leak-freeは未検証
    styled Radio dot未修正、Find names/help/JA不足 確認 public base Radio / search engineでKagi compositionは可能、PRが唯一の道ではない
    Select AXは別API、Input aria_labelは同名で解決 確認・一部非同等 f5のduplicate-placeholder抑止/Find field名まで同じではない
    Undo全置換の境界は公式で解決 確認 Atomic intent+following coalescing boundaryまで追跡
    test-supportは公式にもある 確認・拡張 正式snapshot/query/event APIがありfork覗き見hook維持不要
    本当に無いのは4つ、半分は消える 変更/粗過ぎる 新Clear/IMEとpartial semanticsを含め13commit別に分類。missing API≠fork必須
    Kit migrationが唯一の道、Zed patchはpreへ載替必須 誤り 公式更新+アプリ側public API構成が可能。載替はowner方針にも逆行
    terminalは選択/PtyWrite/mouse/chord/paste/API追従を独自実装 確認 各commitを後述。currentmainのbox renderer差も区別
    upstream terminalの一部testsをKagiが削除 確認 clipboard3/event2の5件。viewのtests omittedコメントはimport時からあり
    terminal release0.1.0のみ/main最後2026-01-11 確認 ただし2月review/7月PR作成あり、『完全停止/放棄』は断定しない
    terminal変更はupstreamへ返せない 断定過剰 gpui0.2.2向けに分離adaptすればPR提案は可能。採用可否は未確定
    Zed terminalはGPLなので代替不可 配布方針として確認 GPL-3.0-or-later。MITと法的非互換という意味ではない
    vendorを『forkでなくKagi code』とすれば解決 誤り 所有責任を明示する効果はあるが由来/派生/配布義務は残る

    fork ごとの判定(commit 単位)

    A. TomiXRM/cosmic-text — 7c9c36a8f87227902d379c59100798c0cb015946

    判定: fork は不要にできる設計だが、端末2行だけでは撤去条件を満たさない。 本体のstatus幅計算と描画外テスト計測もfontを統一する。Linuxでの字形実証は本調査では行っておらず、撤去PRの受入条件である。

    公式mainは f1a3461f3e8df67d2bbcfafb8bc61dc7aee5b2a6、fork branch headは7c9c36a8、親は0.19.0 c24886c2471e5606587c46090cd25dbbf209186b。唯一のfork commitを以下で判定する。PR #522 はstate OPEN、mergedAt null、reviews[]、comments[]、updatedAt2026-07-08T07:00:54Zを今回再取得した。

    #1479のclaim再検証

    • 『0.19.0の上に1commit、unix.rsのみ』: 正しい。ただしcommit全体の差分は +43/-6(tests込み)。Issueの+37/-5という数字はそのまま採用しない。
    • 『ja-JP/ko-KRがbare ja/koと一致せずSCへ』: 正しい。公式current unix.rsのhan_unificationは今も完全一致(下記L58以降)。かなはhardcoded ja、Hanはlocaleという違いも確認。
    • 『localeを - _ . @ で区切る』: 正しい。fork unix.rs:59-84。ただし読むのは最初の2subtagだけで、一般的なBCP47正規化ではない。zh-Hant-HKはTC、zh-HKはHK、case-sensitive。これは現行実装の説明であり追加修正を求めるものではない。
    • 『PR Backend::run: CreateBranchWithCheckout の途中失敗が oplog 記録を迂回する #522 open、未merge』: 正しい。REVIEW_REQUIRED/BLOCKED、updated 2026-07-08。Fix Han unification fallback ignoring locales with region subtags (ja-JP got SC glyphs) pop-os/cosmic-text#522 。PRはhead fix-han-unificationで、Kagiが使う0.19 branchそのものではない。
    • 『公式mainも未修正、最新release 0.19.0』: 正しい。https://github.com/pop-os/cosmic-text/releases/latest 、公式source https://github.com/pop-os/cosmic-text/blob/f1a3461f3e8df67d2bbcfafb8bc61dc7aee5b2a6/src/font/fallback/unix.rs#L58-L73
    • 『gpui_wgpuはLinux/FreeBSDだけ、cosmicはそこだけ』: 前半は誤り。gpui_webもgpui_wgpu::CosmicTextSystem::new_without_system_fontsを使用(凍結Zed gpui_web/src/platform.rs:70-80、Cargo.toml:22)。ただし このunix.rs patchの効き先 はunix非macOS/非Androidで、WASMはother.rsなのでWASMにはこのlocale修正は効かない。cosmic font/fallback/mod.rs:13-26。macOS CoreText/Windows DirectWriteというnative区別は正しい。
    • 『通常UI・コード表示の多くはRootの明示fallback』: 正しいが条件付き。primaryにそのglyphがあるとprimaryが優先される。全CJKを常にJPへ置換する設定ではない。
    • 『残りは端末だけ』: 誤り。下記3を参照。Issue自身が非網羅とした点は解消してからpatchを外す必要がある。
    • 『locale patchだけでは最小構成で再現可能にならない』: 趣旨は正しい。選択先はOSのNoto Sans CJK JPで、Kagi同梱familyはNoto Sans JP。非同梱familyの存在は保証されない。ただし『存在しなければ何の役にも立たない』は強過ぎる断定で、他のfallback探索もある。deterministic JP選択を保証しない、と書くのが正確。

    明示fallbackによる正確な置換箇所

    既存vendorを保守する場合の最小一貫変更は:

    // view.rs TerminalConfig、DefaultはNone
    pub font_fallbacks: Option<gpui::FontFallbacks>;
    // Kagi src/ui/terminal.rs::build_terminal_config
    font_fallbacks: kagi_ui_core::fonts::ui_font().fallbacks,
    // render.rs TerminalRendererにも同じfieldを保持
    // constructor受け渡し、measure_cell/paint両方のFont
    fallbacks: self.font_fallbacks.clone(),
    // view.rs::update_config
    self.renderer.font_fallbacks = config.font_fallbacks.clone();

    FontFallbacksはArc保持なのでglyph hot pathでVec/Stringを毎回構築しない。Kagi側からNoto Sans JPのexisting共通定義を使い、generic rendererへKagi固有literalを埋め込まない。

    更新対象:

    • TerminalConfig宣言/Default view.rs:141-183、renderer field/new render.rs:170-229、view new view.rs:505-510、live update :1208-1220、Font両箇所:247,:633。
    • 実Kagi caller src/ui/terminal.rs:111-132。commands.rs:2082-2114がall retained session + other windowsへconfigをcloneして渡すので、この既存pipelineを維持。zoom commands :1961,1968,1975、theme :2140、theme_select.rs:199、settings_view.rs:553,563は原則API追加不要、live反映テストが必要。
    • exact config literal全caller/docs: vendor README.md:48、src/lib.rs:60,147、src/main.rs:130、src/view.rs:113とDefault。renderer constructor literal/docs/tests: render.rs:54,211,725,768 + view.rs:505。new引数を変えるならすべて移行し旧alias/shimは残さない。
    • status_text_widthのfontと、上記描画外のtest metricsを明示fallbackに揃える。
    • existing fonts.rs Linux testはInter Latin+JP regular/boldのfont id差を検査するだけ。端末primaryがJetBrains/Nerdであるケース、renderer/config clone/update後のfallback、2-cell CJKセル配置は未検査。tests/recovery/terminal_keys.rsの日本語はPTY byte checksでglyph testsではない。
    • patch削除時はCargo.toml comment/patch、Cargo.lock source、ADR-0130/0035、changelogの実際の変更理由も整合。将来検証すべきはsystem CJK fontsなし/SC+JPあり、ja-JP/ja_JP.UTF-8、Regular/Bold、端末新規/既存session/other window、status計測と描画の一致。今回この検証は実施していない。

    注意: ownerの『official crates as-is』を厳密に守るなら、vendorへfieldを増やす案は最終方針に合わない。これは現vendor修正の必要配線説明であり、公式gpui-terminal無変更でこの機能が使えるという提案ではない。

    B. TomiXRM/zed — caf5007618c309a8ea66002d44a9c9ab629a2d35

    判定: upstream の同等修正はまだ無い。ただし fork を永続維持・別 package 名へ移植することが唯一の道ではない。 小規模は公式 ListState の使用法修正、大規模は Kagi が公開 Element API 上にリスト geometry を所有する方針を推奨する。単純な2方式切替だけでは不足する(後述)。

    • 親は 90b3aa0b3bd3b453775b11a386907c7ac9acd997。fork branch head は現在も caf50076。1 commit、crates/gpui/src/elements/list.rs だけ +380/−42、追加 test 257行。22 patch package のうち他21 crateに独自実装差分はない。
    • 新しい公開 API は追加していない。 with_uniform_item_height / reset_with_uniform_height / measure_all は基準 upstream に既存。ADR-0222:9 の「APIが無い」は誤り。
    • 独自変更は①estimate保持(初回layout/reset/splice/幅変更)、②同値estimate no-op、③一度だけの末尾意図、④anchor保持。③は Top + Normal の下方向 scroll_by / wheel が対象であり、全操作ではない。Normal-mode scrollbar drag の end-intent 補正は fork 自体にも入っていない。 set_offset_from_scrollbar は独自差分にない。Tail modeを使うと継続追従へ意味が変わる。
    • upstream の同値設定は tree を再構築し全行を Unmeasured に戻す。ただし既知サイズを hint に残すので、#1479の「高さ情報を全部消す」は言い過ぎ。消えるのは Measured 状態/計測済み保証。
    • 自動width-change枝のanchorは forkでも PendingScroll::Absolute。明示 remeasure / estimate変更の Proportional と同一ではない。ADRの幅変更一般を fractional とする記述も訂正が必要。
    • current Zed main c403bc6af644cd0ab60c90045ac6dc3436218809 の list.rs と registry gpui-pre 0.3.9 の list.rs は byte-identical(SHA256 42704328bf8f1f5c75b0129dfaf4052e475f2de1bcfec3a2c6f92f518c6cb573)。main 全体と registry 全体が同じという意味ではない。基準→mainの差分は +192/−8、follow pause / unconstrained width / nested scroll priority 等で、上の独自挙動は未採用。
    • gh pr list --repo zed-industries/zed --author TomiXRM --state all --limit 100 は空。PR採用可能性を「五分五分」と数値化する根拠はない。

    依存箇所: crates/kagi-ui-core/src/diff_list.rs:47–81、特に renderごとの with_uniform_item_height :71、reset :73、gutter remeasure :75、source/split reset :78。共通接続は src/ui/render_helpers.rs:606–612,674–721,791–806。所有者は main_diff_pane.rs:42–43, file_history.rs:34–35, pr_mode.rs:71–72,101–102, kagi-ui-editor/src/lib.rs:466–467,539–540。可変高threadは render_helpers/row_overlay.rs:86–104 の splice。

    非diffの6箇所も対象: pr_lane.rs:237, issue_conversation.rs:105(更新splice :301–304), tab_view.rs:601, issues_mode/feed.rs:186(splice/remeasure :177–191), inspector_files.rs:140(splice/reset :93–95), operations/oplog_restore_inspection.rs:82。ADR「diffだけopt-in」は誤り。

    独自commit / 公式 list.rs / registry source。

    C. TomiXRM/gpui-kit — 現在13コミット、単純な「約半分不要」では足りない

    Kagi pinと kagi/undo-group 現headはともに 7a116a2019160e6d8091075f88f349a9fe9a2a7e。基準 b004e595cf5de98a73b6b561394a559a94ae1e2a から 13 commits / 34 files / 最終差分 +4384/−338(GitHub固定compare APIのfiles集計)。#1479の12 commits、ee625584、+4361/−330は過去snapshot。現在upstream mainは1188c110で 645 commits ahead(git rev-list実測)、旧628から増えた。

    以下で K = Kagi91d2d105、F = fork7a116a20、U = official main1188c110。Uのlocal rootは /tmp/kagi-fork-review/component/。default fork branchとKagiが使うpatch branchを混同していない。

    #3421 はmerged、2026-10-09 13:51:27Z、squash 0bbd9870ce420af9feacbf948b21d3824d82dc0b。TomiXRM作者のupstream PR searchは total_count:1, incomplete_results:false でこれだけ。fork選択機構全体がmergeされたわけではなくpresentation retentionの一部だけ。PR本文のouter-unmount非対応はコードとも一致する。

    全13コミットの台帳

    +/- は各 commit patch の diffstat(最終 aggregate ではない)。リンクは full SHA の一次資料。

    順 SHA・diffstat 差分目的 Kagi依存/U判定
    1 941f20e6c374ea80d2bab9cd08fc96021280db13 17 files, +3410/-169 Root 論理 group、全投稿 membership、UTF-8 rendered stream、現在の shaped geometry、weak presence、retirement lease、外側 edge-scroll、膨大な回帰テスト。Cargo.lock の Zed rev 更新も含む。 group setter2、viewport marker1。Uに presentation retention はあるが、外側 unmount 跨ぎの同等 API はない。
    2 ae37bfd433781abe44e15edd40867fac1b7b3b2c 5 files, +147/-10 already-focused retained post が outer-list unmount 後でも SelectAll/Copy を受けるよう Root dispatch を登録。選ぶのは会話全体でなく focused post。 直接追加 callerなし。1の会話実装の挙動依存。U participant sweepとは逆の要求。
    3 f7e696f6b057b2b091d9b95b5c12cc0f7dfc4f91 1 file, +8/-0 cached InputState::wrap_row_count() 公開。再shape/allocateしない。 production1、test1。U Editorには display_map().wrap_row_count()。Textareaには direct cached count getterなし、auto_growあり。
    4 b50a079693ee06084f47db29b570f189fd941fc4 1 file, +5/-2 window.on_next_frame closure の Entity strong clone を weak にし、removed input を callback が保持しない。 新API caller0。Uの当該 next-frame経路自体がない。『すべてのInput leakが解消』までは断言不可。
    5 13386100eaca38a5759f69fe4596b59f2dff5141 1 file, +15/-8 Radio の checkbox tick を中央6/14相対径の白い dot にする。 Settings RadioGroup4。U styled Radioはまだ tick。base Radioで Kagi 所有 indicator にできる。
    6 879f3bfa4c7d0cdfd2b91a7f290e1fce744c7f6b 1 file, +1/-1 dot色を固定白から theme.primary_foreground に修正。 5と同じ4群。見た目は未吸収、dependency patch必須ではない。
    7 3dad9fccedc6a858ecb5a0def1b044bfc5750976 9 files, +465/-142 SearchPanelLabels9項目、Findボタン名前/help、Tab/ShiftTab所有、ボタンReturn保護、focus fallback、Button tooltip/bounds test hooks、test-support feature。 label configure6 sites、locale adapter1、probe test2 methods。Uは同等 built-in chrome未実装。ただし公開search engine+公式test APIで Kagi 側実装可能。
    8 406b02f21cb07f31508ebf8e78736859828c92d4 2 files, +86/-3 SelectOptionsへrole/name/value/expanded、実focused rootへ適用、SelectState::is_open。 Select constructor1、4 builder calls+is_open1。Uは label別名、role/value/open自動。
    9 ac2f8ceb088541251d0badca4b81743b5c745c65 5 files, +94/-7 Find padding/buttonsから裏 editorへの編集action leakageを列挙して遮断、pointer paddingはsearchへfocus。labels defaultsで元のlocalization保持。 7と同じcaller群。U cycle_focusだけでは代替しない。
    10 a97efac0bb60228d41c991372d53984f18f5dc6c 3 files, +75/-1 Input::aria_label、focused nodeへのname、renderer name getter、weak rendered input test inventory。 Input builders18全てname指定。U aria_labelあり。fork gettersなしだが正式ElementSnapshotで代替。
    11 f5f682757bd5f5d7d55d3964a7d0d4a3415f80f9 3 files, +9/-6 name=placeholderなら重複placeholderを出さない。Find/Replace field名をwrapper GroupからInput自身へ移す。 Input/Find同上。Uは explicit Input label対応だがbuilt-in Find fieldは未named。Uはnameとplaceholder同一時の抑止も同一ではない。
    12 ee625584d557174d9257eea17107e11fbfe25f98 2 files, +66/-1 History::new_groupをreplace_allの前後へ。timed typingと生成全置換を分離し、前後手入力を消さない。 production replace_all2。UはAtomic intentで実質同等(以下に追跡)。
    13 7a116a2019160e6d8091075f88f349a9fe9a2a7e 2 files, +23/-8 Input清掃buttonへ独立localized名を渡すoptional API。IME marked rangeをunmarkしたEscapeはそこでreturnして検索cancelへ漏らさない。 clear_button_aria_label1。IMEは全Inputに適用されるbehavior。U未実装だが両方Kagi公開APIで置換可能。

    13 commit diffstatの単純合計は +4404/-358(ネット+4046)。この合計には同じ行の再編集が含まれるため、#1479の最終 diff +4361/-330と直接比較して『不正確』とはいえない。今回確認できた固定 compare の最終 file数は34。日付もUTCなら10/9〜14、patch作者のJSTなら10/10〜14で、旧12コミット/10/10〜13は当時の記録。

    2. 正確な production caller 棚卸し

    A. 論理投稿選択
    • src/ui/issue_conversation.rs:121:Root::set_text_selection_group(None)。
    • 同 :140:Root::set_text_selection_group(Some((group,&members)))。
    • 同 :40-95:TextSelectionGroupRetirement field、retire()1 invocation、is_active1 method reference。Drop/activation所有権を結ぶ。
    • 同 :277-284:全 accepted posts の members を発行。外側 listで描かれた postsだけではない。
    • src/ui/issues_thread.rs:136:text_selection_group viewport marker 1箇所、callbackが同じowner.listをscroll。
    • Header、posts、Reply composerは同じ outer gpui::list(同:98-134)。今回の対象は Issues thread。PR会話にこのgroup APIが直接使われると主張しない。
    • docs/adr/0198-read-only-issues-workspace.md:88-159 がcanonical rendered UTF-8、stable ID/source revisions、native geometry retirement、already-focused offscreen SelectAll契約を記録。
    B. wrapped height
    • production1:src/ui/commit_panel_composer_render.rs:33、.wrap_row_count().clamp(3,12)。
    • test1:tests/recovery/commit_panel_viewport.rs:188。
    • #1479にある旧commit_panel_render.rs:405ではなく、現在は上記の抽出済みファイル。
    C. Radio

    Settingsの 4 RadioGroup:src/ui/settings_view.rs:699 graph copy target、:713 toolbar表示、:750 accent source、:965 language。直接Radio::newは0で、RadioGroupのchildrenが内部Radioに変換される。

    D. Find/Replace labels

    search_labels 3+set_search_labels 3 = 6 code sites/3 surfaces。

    • src/ui/conflict_view.rs:1094,1020。
    • src/ui/issues_composer.rs:81,185。
    • crates/kagi-ui-editor/src/lib.rs:1539,1592。
    • locale adapter:crates/kagi-ui-core/src/find.rs:3-19。名前9つ=5 icon controls(previous/next/close/case/mode)+2 text controls(replace/all)+2 input fields。
    • test methods:tests/recovery/editor_find.rs:26 search_panel_probe、:73 clear_search_help_probe。Button.bounds_probe/tooltip_builderはKagiが直接呼ぶのではなくfork SearchPanel内部が利用。
    E. Select

    constructor1:src/ui/settings_view.rs:516。:517-520のrole/name/value/expandedを実triggerへ設定。:503のis_open getterもfork API。Uではこれらをcallerで重複管理する必要なし。

    F. named Inputs

    production Input construction18 sites、全て .aria_label(...)(17 root UI+1 editor crate)。共有helperは多数のruntime fieldsを表すため18は『画面上の入力欄総数』ではない。

    src/ui/command_palette.rs:487-488; commit_panel_composer_render.rs:46-47,87-88; conflict_editor.rs:1567-1568; home_github_list.rs:414-415; issues_composer.rs:401-402; list_filter_strip.rs:367-368; modal_renderers_editor_fs.rs:92; modal_renderers_input.rs:38,77,353; modal_renderers_stash.rs:163-164; pr_fields.rs:352; remote_browse_render.rs:36; settings_view.rs:1139-1140; sidebar_panes.rs:511-512; timeline_row.rs:202-203; crates/kagi-ui-editor/src/lib.rs:3300-3301。

    fork test seams:tests/recovery/input_names.rs:33,36,112(drain inventory3 calls)、:66,100(renderer name2);sidebar_filter.rs:33,124(2);sidebar_search.rs:125(1)。take_rendered_inputs_for_testはtest3 calls、rendered_aria_labelはtest5 calls。これらはnative AXそのものではなくrenderer記録。

    G. Undo replacement

    production2:src/ui/commit_panel_composer.rs:99,113(title/body)。

    追加test/E2E source:src/ui/issues_composer_e2e.rs:235,255,290(3);tests/recovery/blame_identity.rs:106,428,429(3);smart_generation_commit_boundary.rs:91,92(2)。これらは通常の Input::replace_all 呼び出しであり、新しいfork専用呼び出し名ではない。

    H. Clear/IME

    src/ui/sidebar_panes.rs:517 clear_button_aria_label 1。same render_filterがwide/compactに共有される。

    Kagi policy:sidebar_focus.rs:196-218(IMEならunmarkのみ、非空ならclear focus保持、空ならlistへ)と modal_key_routing.rs:254-269(CloseMainDiff fallbackがfocused sidebarを判定)、binding commands.rs:1387-1390。

    3. 公式 API の同等性と具体的 no-fork 置換

    3.1 Input AX:同名APIあり、test hooksは公式観測へ

    U crates/component/src/input/input.rs:271-274 にInput::aria_labelあり。:734-779 は実BaseInputへname/role/value/focus actionを付ける。source。base input/base/mod.rs:143 の実controlは.test_support()登録済み。

    置換:単行はInput/InputState、bodyはTextarea/TextareaState、code editorはEditor/EditorStateへ型を移す。その後それぞれ.aria_label(Msg::...t())を維持。fork renderer getter/weak inventoryは削除し、公式 TestQueryExt::find_all_by_role、find_by_label、TestWindowExt::within/find から ElementSnapshot::{label,role,focused,value,bounds} を読む。

    U crates/base/src/test_support.rs:17-116、crates/kit/src/test.rs:31-79、crates/kit/src/test/query.rs:5-22 は公開API。test-supportはcomponent Cargo.toml:19/kit:27に存在。local GPUI要素は.test_support()を付ければ同じ観測方式。

    限界:Snapshotはframeworkが読んだAX propertiesであり、実OS VoiceOver発話の記録ではない。既存native_ax::press/treeシナリオは残す。help/tooltip文言はSnapshotに汎用getterがないので既存tooltip表示・native AX helpかKagi側観測で確認する。f5のname=placeholder suppressionはU input.rs:734-766に同等条件がなく、placeholderも別にemitする。『a97/f5の全てが同一実装で解決』は誤り。

    3.2 Select AX:labelは改名、role/value/openは自動

    U component select.rs:691 .accessibility_label。render :849-862 はstateからopen/valueを取り、BaseSelectへ渡す。base select.rs:196-207 が実focus rootでComboBox/expanded/label/valueを公開する。source。

    [INFERENCE] Select::new(&state).accessibility_label(Msg::SettingsTheme.t()).menu_width(...).small().w(...) に置換し、.role/.aria_value/.aria_expandedとcallerのis_open計算を消す。Kagi Theme SelectItem::titleは既にlocalizedなので自動valueを使える。ただし現callerはcontrol-byte sanitationを明示している。custom theme由来titleをSelectItem境界で同じsanitizeに通すことは維持する。公式がsanitizeするという主張はしない。

    3.3 Undo:同名 replace_all のまま Atomic で前後を隔離

    単なる『取り消し管理が作り直された』以上に、挙動を追跡した。

    1. U state.rs:1077-1083 replace_all → replace_text。
    2. 同:1139-1151 はprogrammatic full-range replacementに明示的 EditIntent::Atomic をセット。
    3. undo_manager.rs:188-196 はAtomicをpreceding editとcoalesceしない。
    4. 同:237 の coalescing_boundary = intent == Atomic がfollowing typingとのcoalesceも防ぐ。source。

    つまりee625584の『生成前手入力/生成全置換/生成後手入力』の3段Undoを分離する意味は公式で満たす。title/bodyは別state別managerなので独立性も保持。置換callerをset_valueへ変える必要はなく、変えると履歴clear(state.rs:1064)が起きるため誤り。

    3.4 wrapped row count:Editor公開とTextarea不足を混同しない

    U EditorStateだけ display_map()がpublic(state.rs:11833-11838)。DisplayMap::wrap_row_count()はdisplay_map.rs:375-376でpublic。したがってEditorでは editor.read(cx).display_map().wrap_row_count() がfork getterと同じcached countで、getter PR不要。

    しかしKagi Commit bodyは .multi_line(true) のplain input(commit_panel_composer.rs:173-181)で、自然な新型はTextareaState。EditorのgetterはTextareaにはない。TextareaState::auto_grow(3,12)(U state.rs:11715-11718)はwrapped countを内部計算し、layoutでupdateする(base/element.rs:1813-1817)。

    [INFERENCE] no-fork優先案:bodyをTextareaState::new(...).auto_grow(3,12)、renderをTextarea::new(...).aria_label(...)にし、手計算body_height/getterを除去。親composerのflex_shrink(1),min_h,overflow_hiddenを維持し、必要ならauto-grown実boundsを公開on_prepaintで観測してouter preferred heightへ反映する。

    同等性の境界:現Kagiは『内部auto-grow minが外側composerの縮小を阻害しない』ため、cached rowsから外側高さを計算しInputを.h_full()にしている(composer_render.rs:28-35,78-91)。auto_growだけでこのshrink契約まで同一と未検証で断言できない。no-fork代案として現在のbodyをEditorState+line_number(false)/searchable(false)に変えてpublic display_mapを使うことはできるが、mode/key behaviorが変わるため推奨drop-inではない。plain Textareaのままcached countを外側へ無追加shapeで公開したいなら、Textarea/MultiLine public measurement seamの小PRが適切。#1479の『全upstreamに公開なし』は現在のEditorには不正確。

    3.5 Radio dot:styledは未修正、unstyled primitiveは正式な代案

    U component radio.rs:235-240 はまだcheckbox_check_iconを使用。source。

    U base radio.rs:15-19 は『activation/focus/accessibilityはframework、indicator/label/layout/colorsはapp所有』を明示。Radio::checked/disabled/accessibility_label/on_change/track_focus/set_position、base RadioGroup::axis/childrenがpublic。

    [INFERENCE] KagiのSettings用薄いcomponentをbase::Radio+base::RadioGroupで組み、checked時だけrounded inner divを6/14径、primary_foregroundで描く。現在の4 Settings callbacksをそのまま用い、focus ring、disabled opacity、position/sizeと既存keyboard conventionを維持する。dependency forkは要らない。styled Radio自身を全consumerで修正したいならupstream PRにするが、『視覚好みのためfork必須』ではない。

    3.6 Search:cycle_focus が解決するのは2入力だけ

    U search.rs:229-245:replace_mode && replaceableならsearch↔replaceを交互focus、それ以外はreturn。forward/backwardは同じ。renderはsearch_input focus handleをpanelにもtrack(:310-335)。

    U built-in Findは5 icon controls+2 text controlsにhost labels/help APIなし(:353-459)。case/modeのtoggledは既にあるが名前は別。field自身にもhost aria_labelがない。SearchPanelLabels/search_labels/set_search_labels/probeは全crates literal searchと別pattern searchで未発見。locale ui.ymlはEN/中国語/一部IT等でja:なし。

    cycle_focusで解決しないもの:7 buttonsを含むforward/backward順、Enterがancestor editorのFind Nextへ漏れない所有、padding/buttonでBackspace/Paste/Undo/Copy等がunderlying editorに届かないこと、actionがdisabled/消滅した後のfocus戻し、localized names/help、field自身のAX name。

    [INFERENCE] no-fork案はFind chromeをKagiが持つがmatcherは公式を再利用する。U base/input/editor/search.rs はpublic set_search_query(:172)、next_search_match(:191)、previous_search_match(:200)、replace_current_search_match(:211)、replace_all_search_matches(:244)、close_search(:186)、search_session(:136)。state.rs:11592-11607は.searchable(false)でもcustom UIが同engineを使えると明記。

    具体案:editorをsearchable(false)で組み、Kagi-owned panelに2 named Inputs+7 named official Buttonsを置く。query/case change→set_search_query、prev/next→official methods、replace/all→official methods、close→close_search+editor.focus。match positionはsearch_session().matcherから読む。Cmd-F/Cmd-Hをhost actionsへbindし、selected textでquery初期化、reinvokeでfocus/select-allを維持。既存Kagi keyboard navigation/focus trapでpanel内Tab/ShiftTab、Button Return、残余editor actions/padding pointerを所有する。新matcherやraw text replacement実装は不要。

    本当にbuilt-in panelを使い続けたい場合は、label/help options、7 controls focus/Return/containmentのupstream PRが必要。custom host chromeを選ぶならPRはfork撤去の前提ではない。

    3.7 新Clear/IME Escape:両方 Kagi へ移せる

    Clear

    U component/input/clear_button.rs:9-15 はClose icon、text/xsmall/tab_stop(false)のみ、名前なし。input.rs:833-841はこのprivate helperを呼ぶだけ。clear_button_aria_labelはUにない。

    U Input::suffix(:281)とcleanable(:317)はpublic。

    [INFERENCE] .cleanable(false)にし、query非空時のみnamed Buttonをsuffixに渡す。Buttonは独立したMsg::A11ySidebarClearSearch名、Close icon、text/xsmall/tab_stop(false)、必要なtooltip。on_clickでは同じinput entityをupdateして clean(window,cx); focus(window,cx)。空ならsuffix自体を出さず、disabled/loading条件は現在のbuiltin show条件と合わせる。Input自身のaria_labelをそのまま残す。AXPressはofficial Buttonのaction経路を使う。名前をsearch fieldから代用しない。

    IME

    F commit13は unmark後のreturn1行。queryを巻き戻す機能ではない。U state.rs:2205-2214は依然 unmark_text → clean_on_escapeならclean、そうでなければcx.propagate。source。Kagiのsidebar ancestor guardはmarked rangeを再確認するが、childが先にunmarkしたらguardはもうcompositionを見られない。query-clear/ancestor escapeへの漏れはこのsource順序から説明できる(実dispatchを今回GUIで再現してはいない)。

    公開traitでexact policyをhost capture可能:U state.rs:4189 impl<M:InputModeKind> gpui::EntityInputHandler for InputBaseState<M>。:4214 marked_text_range、:4223 unmark_textはtrait methodsとして公開される。unmark_textはmarked=None+undo_manager.commit_transaction、query/focusは触らない。

    [INFERENCE/compile未実施] inputを含むwrapperに既存Kagiと同じ .capture_actionを付ける:

    use gpui::EntityInputHandler as _;
    // inputをcloneしてcapture closureへ。
    .capture_action(move |_: &gpui_component::input::Escape, window, cx| {
        let consumed = input.update(cx, |state, cx| {
            if state.marked_text_range(window, cx).is_none() {
                return false;
            }
            state.unmark_text(window, cx);
            true
        });
        if consumed {
            cx.stop_propagation(); // componentのclean/propagate前にconsume
        }
    })

    通常Escapeは止めない。必要なInput surfaceごとにこのpolicyを共通helperで付け、componentの全Input一律patchだった範囲を明示的に移す。Kagiはcapture_actionを既にcommit_panel_composer_render.rs:57,63で使用。これでforkと同じunmark/consumeが可能なので、IMEをPR-only blockerに分類しない。一般ライブラリ修正として1行returnのPRを提案する価値はあるが、Kagi撤去には必須でない。

    tests/recovery/sidebar_search.rs:50-101 はfirst nonempty Escape、empty exit、marked Unicode Escapeを明示。:105-158 はEN/JA Clear native tree/AXPress/focus/empty removal。今回の『open #1548』metadataと、既にコード/changelogへlandしている事実は別:K CHANGELOG.md:10、decisions.md:18は実装済みと記録する。issueは取得時openのまま。

    D. vendor/gpui-terminal — Kagi-owned adapterとして責任を明確化するが、派生物の事実は残る

    推奨: 公式 alacritty_terminal / portable-pty を無改造依存とし、既存のGPUI接続・安全な貼付・選択等をKagiの保守境界として扱う。既存vendorの再利用自体は許諾されるが、単なる改名でforkが消えたとは数えない。 公式gpui-terminalをそのまま戻すと必須機能が欠落する。

    公式main 51f0292938876c8da3de03f0139088591e3be518、公開0.1.0のsource 45c63e57181d27c260124a81c7e4b68a6b6e57b0、公開日2025-12-24。以下は公式との差分・Kagi内13 scoped commitをすべて記載する。上流commitの停止と、作者の活動停止を同一視しない。

    公式公開版・main・Kagiの差

    公開0.1.0→current mainの一次diff:
    zortax/gpui-terminal@45c63e5...51f0292
    mainは0.1.0表記のまま、GPUI 0.2.2、alacritty_terminal 0.25.1。公開版から2件のrendering commitとREADME updateがある。M→│によるセル計測、1.0 line-height、864行のbox_drawing module、水平span/vertical component描画など。Kagiはこのmain rendererではなく公開版由来M/font-glyph rendererに独自機能を足したもの。差分をすべてKagi独自修正と数えてはいけない。

    current mainの未実装はview.rs:735-792のmouse選択/scroll TODO、event.rs:147-149でPtyWriteを握り潰す処理、view.rs:821-823のClipboardLoad TODO。Kagiは選択・scroll・PtyWriteを実装したがClipboardLoad TODOはまだ残る(vendor view.rs:1099-1101)。appのbuild_terminal_viewはresize callbackしか設定せずOSC clipboard_store callbackも設定していない(src/ui/terminal.rs:539-551)。READMEの『OSC52 clipboard integration』を全面対応の証拠にしない。

    API差:

    • TerminalState::process_bytes(&mut self, &[u8]) -> Vec<u8>(vendor terminal.rs:227)、officialは()->()。GpuiEventProxy::pty_responses_handle API追加(event.rs:150)。reader taskはreturned bytesを書戻してからnotify(view.rs:538-553)。
    • renderer paintにselection parameter追加(render.rs:484-492)、ColorPalette.selection/accessor/builder、mouse reporting/scroll helpers追加。
    • TerminalView::paste_text(&str,bool)->boolとpaste_contains_control_characters API追加(view.rs:1154-1200)。viewの選択抽出/geometryはprivateで、公式view外側の薄いwrapperで同じ能力を得る公開APIはない。
    • GPUI git版/分割gpui_platformへの対応とquad/text paint引数変更。公開gpui 0.2.2との型identity差もあり、単純Cargo dependency置換だけでは接続できない。

    PR/activity:

    6. vendor scoped Kagi commits(初回importを含む13件)

    完全listing一次資料: https://github.com/TomiXRM/kagi/commits/91d2d10570cd88fc8e43151c8bb0a63003bb2a4a/vendor/gpui-terminal/
    各SHAの https://github.com/TomiXRM/kagi/commit/ と .patch を確認。日付はpatch author dateの日本時間表記(GitHub commit一覧の日付timezoneと1日違う場合あり)。

    出自/import

    • 2026-06-12 00c43470e4b816ab3d116e1de19e4805667a3d12: gpui-terminal0.1.0全体vendoring、MIT/Apache license・cargo provenanceをそのまま保持。view.rs末尾『macro issueでtests omitted』コメントは初回importに既に存在し、Kagiがview testsを削除した証拠ではない。

    挙動差:9件

    • 2026-06-12 0ccae1a9bb0b68e65e78f68120e349db4963d43e: 選択/Cmd copy。colors.rs selection、mouse.rs selection helpers、render.rs highlight/paint API、terminal.rs extraction/search tests、view.rs mouse state/paint geometry/key handling。vendor以外にapp terminalも変更。
    • 2026-06-16 7774a843cd292af6faab6d551bd156b978046c09: render.rs display_offsetでhistory描画/cursor条件、view.rs scroll_display。shell login/interactive argsはKagi app側。
    • 2026-07-07 cd36c0e865a15031e51f87dfced553957894e6d5: input.rs platform chordのPTY文字漏れ防止、tests、manifest dev dependency。
    • 2026-07-07 967ee1f0c3f27db7d5be44ff89b8089f65bb8f71: 前guardの過剰抑制を修正。cmd-backspace→0x15、cmd-left→ESC[H、cmd-right→ESC[Fを例外化 + test。editor remediation全体commitのうちvendorはinput.rsのみ。
    • 2026-07-07 abff9a3eb7c58151c3f943bd1f477590a1a01e88: event.rs queue、terminal.rs process_bytes戻値、mouse.rs SGR/legacy modifiers/button/motion/scroll dispatch、view.rs即時response書戻し/Shift bypass/alt-screen scroll等。+704/-52のうちdocs ticket128行を含むため全部実装量ではない。
    • 2026-10-13 9f75e5603b35ea593b81cf3f8e7c5fd346ff5afc: vendor view.rs paste_text追加、DECSET/DECRST2004をemulator stateから参照、plain改行はunapproved時zero-write。確認modal、owner/spawn、raw Tabとclipboardの分離はKagi側。
    • 同日 d5c005e5ddfc6c2301cc013f710d432c05d1ca7b: bracket payloadのESC/ETXをallocation-free retained slicesで除去。CR/LF/他UTF8を保持。app実PTY respawn coverage。
    • 同日 5ff33c0bfad36d0c4a4e74ea9802c14fa65f516f: plain C0/DEL確認guard+shared predicate、localized warning/実PTY tests。
    • 同日 dfccc4a508b5b2e189930050481dae33329ba561: predicateからclipboard Tabを除外、CR/LFは別warning、docs/testを整合。現在の仕様はC0/DEL『except Tab』。

    機械的移行/format:2件

    • 2026-06-14 a8a6be4664a6af25308e09a698dd73b485b0fdc4: vendor render/view formatting/clippy cleanup。他多数ファイルのcommit。
    • 2026-07-14 287b46ebe8226c161a7cd5f8712f943e1ece4207: Cargo manifest GPUI git/分割platform、main.rs Application、render.rs paint signature、view.rs quad差への対応。独立のterminal機能追加ではない。

    test削除:1件

    • 2026-10-09 8853fcff4e1b5e7e4653ce779a2f0f479c6d412a: clipboard.rsのtest_clipboard_creation/copy_paste/clear 3件とevent.rsのtest_clipboard_store_event/load_event 2件を削除。後者2件はflume/proxy上のunit testsでOS clipboardを操作していないので、5件全部を『native clipboard isolationのため必然』と説明するのは不正確。production clipboard API/enum処理は残る。mouse/input/selection/query等Kagi追加testsは残っており『全test削除』でもない。

    7. 所有・license・公式crate方針の推薦

    「GPLなのでMITと法的に非互換」と解釈するのは誤り。#1479の不採用判断は配布方針として読む。 current terminal/terminal_view Cargo.toml:6は GPL-3.0-or-later(GPL-onlyではない)、各LICENSE-GPLはroot GPLv3本文参照。https://github.com/zed-industries/zed/blob/c403bc6af644cd0ab60c90045ac6dc3436218809/crates/terminal/Cargo.toml#L1-L6 と同commit crates/terminal_view/Cargo.toml、root LICENSE-GPL。
    MIT/ExpatはGPL compatible: https://www.gnu.org/licenses/license-list.html#Expat 。MITコードを含むGPL配布は可能だが、GPL-covered terminalをリンク/転写したcombined workを配布するならGPL条件・Corresponding Source・notice等が問題になる。非GPL/MITのままのKagi配布方針とは両立しない、という政策上の却下が正確。ADR-0031の『非GPL配布想定』は根拠になるがStatus Proposedであり、法律上の一般的禁止ではない。設計概念の参考とGPLコード転写も区別する。

    選択肢:
    A. 公式gpui-terminal0.1.0/mainをas-is: permission licenseは合うが上記要求を欠き、現在Kagi GPUI型とも違う。今の挙動を維持したまま単純に採用する案は推薦しない。
    B. 公式alacritty_terminal + portable-ptyを無変更依存にして、GPUI adapterをKagiコードで保守: existing Kagi session/supervisor/resize/approvalをそのまま利用。Rust/UI部だけ TerminalWidget { term, parser, writer, selection, font, geometry } としてKagiの責任境界を明確化。parser response→writer before notify、emulator mode authoritative、paste framing/filter/approval、selection/scroll/report、フォントとCJK幅を実装/移行対象に含め、現在の挙動を削らない。alacritty licenseはApache-2.0: https://docs.rs/alacritty_terminal/0.25.1/alacritty_terminal/ と https://github.com/alacritty/alacritty/blob/master/LICENSE-APACHE 。portable-pty existing APIはそのまま利用できる。これは『official crateを直す』のでなくapp-owned adapterでありowner方針に最も近い。
    C. existing vendorをKagi-owned crateへ移す: 保守責任は明瞭になるが 外部コードの派生物/実質forkである事実は残る。単なるname/path/license変更はfork解消でもupstream採用でもない。コードを再利用するなら作者copyright/原license/由来commitを保持しなければならない。original .cargo_vcs_infoを現ローカルcrateの現在commit証明として誤表示しない。provenanceを専用notice/manifest metadataへ記録する設計はあり得る。
    D. 別emulator: wezterm-termはMIT、公開source https://github.com/wezterm/wezterm/blob/main/term/Cargo.toml と LICENSE.md。ただしGPUI adapterは別途必要でdrop-inではない。COSMIC terminalはGPL-3.0-only(GPL-3.0-or-laterではない)、https://github.com/pop-os/cosmic-term/blob/master/Cargo.toml#L1-L12 と LICENSE。MIT-only方針の代替にはならない。

    vendor reuseの義務:

    • vendor/gpui-terminal/LICENSE-MIT:1-19: Copyright 2025 Leonard Seibold + permission noticeをcopies/substantial portionsへ含める。MIT OR Apache-2.0なのでMITを選択できるが、作者表示をKagiに置換してはならない。
    • Apacheを選ぶならLICENSE-APACHE:89-120(section4): license copy、変更fileにprominent change notices、著作権等notice保持、NOTICEがあればその内容維持。https://github.com/zortax/gpui-terminal/blob/51f0292938876c8da3de03f0139088591e3be518/LICENSE-APACHE
    • Kagi packagingのthird-party notice/ライセンス同梱を明示。今回source検索では専用NOTICE/THIRD-PARTYファイルを発見しなかったが、配布物をinspectしていないので『現在の配布が違反』とは断定しない。
    • Noto同梱fontはSIL OFL 1.1(ADR-0130)。terminal owner変更でfontのlicense/attributionが消えるわけではない。

    結論: cosmic forkは 端末+status+test metricsの明示font統一を終えてから official0.19.0へ戻せる設計見込み [INFERENCE]。WASMは別途同梱font問題として分離。terminalは公式gpui-terminalの単純置換で保持できる機能ではなく、official emulatorを使うKagi adapterか、由来/notice付き派生vendor保守という選択を明示する。『フォークではなくKagiのコード』というIssueの言い換えだけを解決として採用しない。

    scoped commitごとの移行判定

    • 00c43470(import): releaseのライセンス/由来を維持。機能patchではない。
    • 0ccae1a9(selection/copy)、7774a843(history scroll): official viewに同等完成経路なし。Kagi-owned adapterでalacrittyのselection/display_offset+GPUI hit/paintを使う。既存view外側だけのwrapperはprivate geometry/stateのため不十分。
    • cd36c0e8、967ee1f0(platform chord guardと例外): Kagi keyboard boundaryでCmd入力をPTY文字にしない。Cmd-Backspace/Left/Rightは先に対応byte/actionへ解決し、残りCmdはconsume。公式ライブラリへのpatchは不要。
    • abff9a3e(query/mouse): adapterのparser feedで生成responseをwriterへ渡してからnotify、emulator modeに従うSGR/legacy mouse・Shift bypass。opaqueなofficial TerminalViewをそのまま維持したい場合のみupstream対応が必要。
    • 9f75e560、d5c005e5、5ff33c0b、dfccc4a5(paste4段): current emulator modeをtruth sourceに、Kagi側confirm/preflightとadapter側framing/filterを維持。unapproved plain改行/C0/DELはzero-write、Tab例外、bracket payloadのESC/ETX除去を順序込みで移す。一般文字列を送るだけのofficial pasteに戻さない。
    • a8a6be46(format): 挙動移植対象なし。287b46eb(GPUI供給元/paint API): #436でregistry preへ全caller同時移行。旧source互換shim不要。
    • 8853fcff(5 tests削除): upstreamへ要求すべき機能ではない。event proxy2 unit testsまで消えている事実を受入coverageに反映し、テスト削除をfork維持理由や移行成功の証拠にしない。

    全てをofficial TerminalView as-isで実現したいなら、private state/APIを開く上流変更が必要。しかし現在の責務をKagi-owned adapterへ置く案では、そのPRは撤去前提ではない。maintainedな外部emulatorを使うことと、UI adapterをアプリ側で持つことを区別する。

  2. TomiXRM commented on Oct 14, 2026

    @TomiXRM
    OwnerAuthor

    (続き)

    §8 の検証結果

    §8(a) 新しい選択機構と外側virtual-listのoffscreen投稿

    4.1 公式の新機構が実際に保持するもの

    U public TextSelectionHandle(base/text_selection.rs:800-1017)にはfallback text、copy_with、subscribe、register、scope/document_order、content-key resolverがある。Copyは文書順へsortする。したがって『upstreamに横断選択の仕組みが全くない』は誤り。

    しかしregisterはgeometryを持つpaint participant。TextSelectionRegistration::with_rendered_element(:244-270)はGPUI keyed element stateのweak markerを取る。cached replayでpaintしないframeもmarkerがliveなら保持する。実outer virtual unmountではmarkerがなくなり、finish_frame(:1282-1320)がparticipantをregistryから削除し、そのlocal selection/clear handlerを発火する。

    Copy copy_items(:1523-1533)は残ったregistry entriesだけを列挙する。endpoint resolve(:1051-1056)はparticipant登録とweak ownerを要する。TextViewはprivate TextViewSelectionAdapter(base/text/state.rs:129)が自分のhandleを持ち、TextView.paintが登録する(text_view.rs:893-901)。retainしたTextViewStateだけではpaint participantのmarkerを延命しない。

    結論(コード上の事実):Entity<TextViewState>を会話全投稿で保持する現在のKagi設計を、公式UへAPI名置換するだけではoffscreen selectionは保持できない。public copy_withの存在だけで、registryから消えた投稿がCopyへ入ることもない。ContentKeyはgeometry endpoint内のstable block identityであり、全unmounted投稿membershipの代替ではない。

    4.2 単一TextView内のoffscreen blocksは別に対応済み

    U TextViewSelectionAdapter::VirtualBlockSelection(selection_adapter.rs:16-77)はendpointにblock indexをcacheし、coverage Full/FromStart/ToEnd/Boundedでrangeを復元。ParsedDocument::selected_text(document.rs:91-154)はrange内部の未paint blockをwhole rendered textで埋める。大きい単一TextViewの内部virtual blocksは扱える。

    だから『#3421の制限→どんなoffscreen選択も不可』も誤り。正しくは『outer listが独立TextView participantをunmountする場合は不可、retained participant内のinner virtual blocksは対応』。

    4.3 Fが追加した決定的な違い

    F logical_selection.rsのGroupはstable post IDs+weak TextViewState+accepted rendered revisionの全membershipを持つ。Endpointはpost/revision/leaf/UTF8offset/affinity。paint geometryはhit authorityだけで、Copy sourceではない。logical_selected_textはstart..endの全membersをwalkし、各parse-owned RenderedDocument::write_rangeを読む。中間投稿は全canonical rendered bytes、端の投稿は正確なpartial range。未paintでも文字列は完全。viewport leaseはlive必要で、unmounted inline geometryだけは失効する。

    ae37bfd4はfocused postを保持するdispatch fallbackも別途追加。公式はこのunmounted dispatch authorityを公開していない。

    4.4 fork無しの具体案と、その限界

    案A:一つのretained TextViewへ会話をまとめる [INFERENCE]。

    PreparedBodyをreading orderで一つのmanaged Markdown documentへ投影し、author/time/chromeはblock pluginで描き、single TextViewの内部virtualizationへ寄せれば4.2の公式range/copy engineを使える。Reply composerは同scroll surface末尾へ配置する必要がある。no outer post unmountなので問題の経路を消せる。

    ただし現行契約のdrop-inではない。単一source更新は選択をinvalidateするため、未選択投稿のprepend/reorder/editでselected true Bだけを維持するKagi契約を、そのまま満たすとはいえない。Ctrl/Cmd-Aも単一文書全体になり、既存focused postだけ選ぶ意味が変わる。author chromeのCopy除外、画像/link/literal-codeのcanonical bytes、one-scroll composer、stable anchor/per-post accepted reuseも設計し直す。これを『そのまま撤去可能』としない。ユーザー承認なしの選択機能削減も提案しない。

    案B:Kagi所有のdocument adapter/rich component [INFERENCE]。

    official base TextSelectionHandleのpublic登録/subscription/copy callbacksを使い、Kagiがstable accepted document+logical interval+viewport lifetimeを所有。Kagi-owned rendererがvisible runsだけgeometryを供給し、Copyは全accepted rendered streamを読む。これならdependency forkは不要で現契約を維持できるが、公式TextViewのprivate selection adapterに外部handleを差し込むAPIはない。現在のstyled TextViewをただwrapするだけではexact shaped rendered endpointsもnon-mutating whole rendered textも取得できない。raw Markdownをconcatしてコピーする『代替』は契約違反。local rendererを所有するなら大きな保守負担として明示する必要がある。

    案C:必要なextension seamをupstreamへ。

    独自forkを避け、現在のper-post TextView/reconciliation/Copy契約を維持したいなら、次はTextViewに外部logical document adapterを接続する小さいpublic seamの提案が必要。提案sketch:post stable ID/revisionでcanonical rendered text/rangeを供給するdocument owner、visible TextViewがshaped caret offsetをそのownerへ報告するadapter、paint registrationとは別のlogical membership lifetime、explicit retirementとscope、outer viewport scroll callback。『全unmounted geometryを永続register』ではなく、論理authorityとhit authorityを分離する。#3421既存のpresentation retentionはそのまま再利用し、3.4k行fork丸ごとの移植とは分ける。

    exact current behavior保持+official TextView as-is+outer per-post virtualization維持の3条件を同時に置くなら、Uの公開APIだけでは埋まらないseamがここにある。これが今回確認した本質的なupstream提案対象。PRがacceptされる確率はsourceから推定しない。

    Kagi回帰契約:tests/recovery/issue_conversation_selection.rs:27-57 はforward/reverseのmousemove→Copyが次paint前でも全postsのcanonical bytesであること、:67-107 は180段落giant body、:116-170 はtrue comment ID reorder/prepend保持とselected same-byte-length mutationの同期retire。lifecycle suiteのsession departure/owner drop/modal/Input priorityも削らず維持する必要がある。

    §8(b) measure_all の実測と、大規模リストの代替

    実測済み。数千行なら大丈夫という前提は採用できない。 ListState::measure_all() 自体は測定モードを設定する builder であり、下表はそれを実行した 次の draw の CPU 時間。全行 element生成・CoreText shape/layout・prepaint・scene組立を含む。GPU present/FPSや Kagi 全体の応答時間ではない。

    行数 初回 cold中央値 cold min–max 同幅warm 1000→600px resize resize後warm 全測定後End reset後
    1,000 17.656 ms 17.168–18.223 0.717 16.132 0.607 0.237 15.413
    5,000 87.998 ms 87.267–90.723 2.679 83.335 2.844 0.457 77.996
    20,000 399.904 ms 382.568–405.909 13.776 345.734 13.424 1.140 335.158

    条件:

    • Apple M1 Pro、MacBookPro18,3、10 CPU、32 GiB RAM、macOS 26.6 build25G72、Rust 1.97.1、aarch64-apple-darwin、release opt-level3/debug0。
    • 公式無改造 gpui-pre=0.3.9 / platform=0.3.9。mainとlist.rsがbyte-identicalであることを確認してからこの公開版で測定。
    • public TestAppContext::build_with_text_system(TestDispatcher, native.text_system()) + native CoreText。MacPlatform headless、OS window無し、foreground activation無し。mock text shaper ではない。
    • diff_view.rs:535–702 に合わせた unified風の独立 row renderer: Menlo、text_sm、2×44px gutter、2 highlight span、EN/JAのRust行、7行に1行3倍長、40行に1 hunk、113行に1長いreview card。viewport1000×700→600×700、overdraw400px。実Kagi rendererの直接ベンチではない。
    • 各Nでfresh TestAppContext/ListState 3試行。GPUI cacheはfresh、OS font cacheはprocess共有。データ/view初期構築は計時外。cold render呼出は N+14、warm14、resize N+9、resize-warm9、reset N+9。可視行は全測定後さらに描画用に生成される。
    • 証拠: /tmp/kagi-fork-review/list-bench/{src/main.rs,src/bin/lifecycle.rs,Cargo.toml,Cargo.lock}、成功ログ artifact://14972。独立 target は同directoryの target/。
    • コマンド: env CARGO_TARGET_DIR=/tmp/kagi-fork-review/list-bench/target cargo run --release --manifest-path /tmp/kagi-fork-review/list-bench/Cargo.toml --bin kagi-list-measure-audit、同じ引数で --bin lifecycle。
    • 最初のbuildは xcrun metal 不在で失敗→公式 runtime_shaders feature で解決。最初のdrawing harnessはEntity Render無しでcurrent_view panic→公式testと同じEntity Renderで修正し、上記2実験がexit0で完走した。ライブラリのpatchはしていない。

    lifecycle実験が示した、単純な代替案の穴:

    1. 1000行(通常40px、末尾400px)、viewport100px。uniform hintは初回draw前のmax40000pxから、初回layout後max20pxへ縮む。公式の初回prepaintもhintを失うため、resizeだけ対策しても不足。
    2. そのthumbを末尾へdragしても top=item0/20px、真のtailは未描画。明示 scroll_to_end() は top=item999/300pxにして400pxのtailを下端100pxへ正しく合わせた。しかし他行が未測定なので is_scrolled_to_end() は依然 None。==Some(true) を末尾到達の唯一の根拠にできない。
    3. 全測定後のoffscreen row10を40→300pxへ変更し splice(10..11,1)。max40260→40220px、末尾判定None。次drawは可視行だけで、offscreen新規行の高さが0のまま。state.clone().measure_all() を再度呼ぶとmax40520pxへ回復。splice は measuring_behavior を再armしない(公式 :511–549)。性能ベンチでも同様にextentが69.5px減り、末尾判定Some(true)→None。
    4. 幅変更で300px行が600pxになると、offset150pxは150pxのまま(50%→25%)。変更前の公開 remeasure() により比例anchorを用意した場合は50%が保持された。

    小規模の置換 sketch(diff_list.rs:47–81 / row_overlay.rs:86–104):

    // 初期化時。一度設定したmeasure-allはresetでも再armされる。
    let state = ListState::new(count, ListAlignment::Top, overdraw).measure_all();
    // 同じsourceのpaintでwith_uniform_item_heightを呼ばない。
    if source_changed { state.reset(count); }
    // expansion、offscreenの同数置換も含む変更点で:
    state.splice(range, replacement_len);
    let _same_shared_state = state.clone().measure_all(); // 新規Unmeasuredを次frameで測る
    // width/font/zoom/gutterの変更前に:
    state.remeasure(); // anchor準備。後続layoutが全extentを再構築

    1kでもこのモデルは60Hzの16.7msを超える。普遍的な「5k以下」等の閾値は提案しない。軽量固定高・短文なら異なるため、実row構成と許容latencyで閾値を決める。

    大規模: scroll_to_end は明示End/one-shot意図の実行には使えるが、正しいestimated extent、中間thumb→行index変換、splice、幅変更を解決しない。is_scrolled_to_end を組み合わせても足りない。したがって #1479の二方式だけで全契約を満たす案は 否。

    最小fork方向の設計 [INFERENCE・未実装]: Kagi側で単一の DiffListState(stable RowKey、source/layout generation、Measured/Estimated高さ、prefix sum、anchor={RowKey,intra_row_fraction}、one-shot End、drag中extent)を持つ。GPUI ListState内部と二重所有しない。可視範囲+overdrawだけ既存row rendererを構築・計測し、実測差をprefixへ反映。source変更のみreset、spliceはRowKey anchorを保持し新規行をestimate、幅/font/gutterは世代invalidate。Endは最後から逆に測ってviewportを満たし、oversized tailも下端を合わせ、意図を消費する。

    公開APIで実現する範囲:

    // 新しいKagi-owned elementの骨格を示す設計表記(実装済みコードではない)。
    // request_layout: window.request_layout(style, children, cx)
    // prepaint(bounds): prefix inverseで可視keyを決定;
    //   child.layout_as_root(size(Definite(width), MinContent), window, cx);
    //   実高を更新しanchor補正; child.prepaint_at(origin, window, cx)
    // paint: window.with_content_mask(...), child.paint(window, cx), wheel listener
    // 公開ScrollbarHandle: viewport_bounds / offset / set_offset / content_size /
    //   start_drag / end_drag。drag開始extentを固定し、thumb末尾はone-shot Endへ。

    GPUI公開面: src/element.rs:52–105,670–710, window.rs:4163,5209,5295,5432。公式 component の base/src/scrollbar.rs:69–82 が公開 handle trait。render_helpers、全diff owner、keyboard reveal、selection、thread expansionを同じstateへ切替える。非diff6 callersは本当に固定高のものだけ公式uniform_listへ、残りは可変高ownerへ。Zed list.rsを丸ごとコピーして隠れたforkを作る案ではない。現在はこの置換を実装・E2Eしたわけではないので、zero-patchへの設計上の道と受入ゲートを分ける。

    §8(c) 端末以外のno-fallback CJK経路

    GPUI根拠:

    • font(family)はfallbacks:None(凍結gpui/src/text_system.rs:1068-1082、current official同file:1317-1327)。shape_lineに渡したTextRunを祖先styleで補完する処理はない。
    • Styled::font_familyはfamilyだけをrefine(gpui/src/styled.rs:708-711)。Fontをstyleへ渡すfont setterはfallbacksも取り込む(:720-735)。TextStyle::fontはその時点のfallbacksをコピー(style.rs:539-549)。
    • Window::text_styleは現在のstackから毎回合成(window.rs:1979-1986)。with_text_styleはclosure中push、終了時pop(:3210-3221)。Rootはwindowの永続default font設定ではない。 描画外のcx.update_windowでwindow.text_styleを呼んでも、Rootのdraw stackが残っているとは考えられない。
    • explicit fallbackはCosmicTextSystemでgraphemeごとのcoverageに基づき選択。current official https://github.com/zed-industries/zed/blob/c403bc6af644cd0ab60c90045ac6dc3436218809/crates/gpui_wgpu/src/cosmic_text_system.rs#L1050-L1140 。resolve_fontの『primary familyがloadできなければfallback familyを試す』機能と、missing CJK glyphのfallbackは別物。

    経路一覧:

    1. native本window src/ui/mod.rs:3516、settings settings_window.rs:35、E2E app root e2e.rs:894: ui_font()をRootへ設定。component Rootはview、tooltip、native_menu overlayを同じinner childとして描画(凍結gpui-kit crates/ui/src/root.rs:660-676)。通常label/SharedString、modal、tooltipはこのcascadeでカバー。
    2. family overrideのみのnative表示: conflict_editor.rs:878,1168,1215,1437,1565、render_helpers.rs:673(main diff)、kagi-ui-editor/src/lib.rs:3291(editor)、snapshot.rs:255、markdown.rs:480、kagi-ui-ecosystem/src/lists.rs:279、pr_conversation/hunk.rs:82、terminal/input.rs:115(paste warning)ほかmodal/metadata/Home/settingsのfamily変更。fallbackを消しておらずRoot配下では同梱Noto stackを維持。InputStateは作成時window styleでdisplay mapを作るが、Input elementのprepaintで現在style.font()を再適用してからprepare(gpui-kit input/state.rs:475-493、input/element.rs:1509-1518)。したがって実描画はRoot inheritanceを使う。TextViewもwindow.text_style由来fontでlayout/shaping(component text/text_view.rs:506-507,652-658)。
    3. 端末の実glyph描画: render.rs:633はfresh Font fallback None。Rootは届かない。:247は同じfresh FontでMのセル幅計測だが MはASCIIなのでそれ自体がCJK描画不具合ではない。設定一貫性のため両方変更する。
    4. 実運用status幅計算: src/ui/render_status.rs:87-103のstatus_text_widthはgpui::font(UI_FONT)をTextRunへ直渡し。Root配下で呼ばれてもfresh Fontなのでfallbackは付かない。表示labelはRootのfontを使うため、計測と描画のfamily選択/advanceが一致しない可能性 [INFERENCE]。ここもui_font()へ統一が必要。
    5. fresh Fontを使うテスト計測: tests/recovery/status_bar_fit.rs:193-212、time_locale.rs:100-121。後者は日本語relative timeをMONO_FONT/fallback Noneでshapeする。
    6. window.text_style()を描画外で読むテスト計測: density_rest.rs:97-123、finder_scale_editor.rs:88-95、finder_scale_history.rs:77-85、finder_scale_rest.rs:136-141、home_heading.rs:65-73、inspector_scale.rs:90-98。これらはcx.update_window closureでstyleを作り、familyのみを変更。上記GPUI stack semanticsからRoot inheritanceはここを保証しない。日本語width/glyph boundsのテストを実表示と揃えるなら、明示ui_fontのfallbackをstyleへ入れるか、paint probeが実font/fallbackを記録する必要。これはtestコードのcoverage差であり、『本番コード表示もすべて壊れる』という話ではない。
    7. WASM catalog: kagi-web/src/lib.rs:14-30はRootなしfont_family("Inter")、:120-130はInter Regular/Boldのみadd_fonts。gpui_webのbundleもIBM Plex/Lilexのみ。native Noto/UI stackはここをカバーしない。現行story stringsは英語主体で、実際のCJK描画失敗を実行確認したものではない。CJK storyを扱うならNoto登録とui_font相当の明示fallbackが必要。unix locale patchでは解決しない。
    8. kagi-ui-core/src/diff_list.rs:93のTextStyle::defaultはline_height_in_pixelsの推定にしか使わずshapingしない。commit_row.rs:345-372のwindow.text_styleはpaint probe内なのでRoot stackを見られる。これらをCJK no-fallback描画経路と数えない。

    検索範囲はKagi src/crates/tests/vendor、Font/FontFallbacks/TextStyle/TextRun、shape_line/shape_text/layout_line/line_wrapper、font_family/font setter、Root生成。source全体で追加の独自shape_line経路は上記status・font test・terminal以外には見つからなかった。

    §8(d) 検索バーkeyboardとcycle_focus

    コードで決着した。公式 component/src/input/search.rs:229–245 のcycle_focusは replace modeかつreplaceableのとき2入力を交互focusするだけ。通常find-onlyではreturn、forward/backwardの順序も同じ。7 buttonsを含むTab順、Button Enterを裏Editorに漏らさないこと、padding/Buttonで編集actionsを止めること、disabled/disappeared targetからのfocus復元、names/help/JAは賄わない。

    no-fork代替は、built-in panelを .searchable(false) で無効にしてKagiに2 named Inputs+7 named Buttonsを置き、matcher/replaceは公式 set_search_query / next_search_match / previous_search_match / replace_current_search_match / replace_all_search_matches / close_search / search_session を再利用する。3 surfaces・6label設定をこのhostへ移す。full sketch・API行番号はKit節3.6。独自regex検索やraw replacementを増設する必要はない。

    §8(e) Radioのupstream受入可能性

    styled Radioがcheck iconであることと、base Radioでdotを構成できることは確認した。upstream maintainerがdotをbugfixとして採用するかはソースから決められない。本調査ではPRやDiscussionを投稿していない。採用待ちをKagiのfork撤去条件にはせず、必要ならpublic base primitiveで既存見た目・AX・keyboard契約を維持する。

    移行計画

    公式依存・zero-patch の成立範囲

    技術的に ZERO [patch] は可能。ただし現行 Kagi の manifest から patch 行だけ消すことは不可能。 fork 固有の選択・検索 chrome・テスト seam・リスト使用法等を先に解消し、GPUI と component の型境界を一括移行する必要がある。

    独立した /tmp/kagi-fork-review/resolve/Cargo.toml(Kagi tree 外、patch 無し)で次を実行した:

    • cargo metadata --format-version 1: 成功、855 packages を解決。公式 upstream Kit 1188c110… + registry gpui-pre =0.3.9、全 tree-sitter languages と test-support、および web/wgpu の target 依存を含む。
    • cargo tree --target all --edges normal,build -i gpui-pre: active core は gpui-pre 0.3.9 一種類で base/component/assets/platform/macOS/Linux/Windows/Web が共有。
    • cargo tree --target all --edges normal,build -i gpui-fast: nothing to print。metadata/lock に optional fast package が載ることと active な二重 GPUI は別。gpui-fast feature は採用しない。
    • 保存物: /tmp/kagi-fork-review/resolve/{Cargo.toml,Cargo.lock,metadata.json}。これは dependency resolver の証拠であって Kagi build の証拠ではない。
    # 7 manifests 全てで同じ source に統一。workspace.dependencies へ集約してよい。
    gpui = { package = "gpui-pre", version = "=0.3.9" }
    gpui_platform = { package = "gpui-pre-platform", version = "=0.3.9", features = ["font-kit", "x11", "wayland", "runtime_shaders"] }
    gpui-component = { git = "https://github.com/longbridge/gpui-kit", rev = "1188c11003b5cb5b83bc07d3fb9112e7674fbfa7", features = ["tree-sitter-languages"] }
    gpui-kit-assets = { git = "https://github.com/longbridge/gpui-kit", rev = "1188c11003b5cb5b83bc07d3fb9112e7674fbfa7" }
    # target 別に必要な既存箇所だけ:
    gpui_web = { package = "gpui-pre-web", version = "=0.3.9" }
    gpui_wgpu = { package = "gpui-pre-wgpu", version = "=0.3.9", features = ["font-kit"] }

    根拠: upstream Cargo.toml:62–79、crates/component/Cargo.toml:17–21,120–131。上流 root の [patch.crates-io] rquickjs は shell workspace 用であり、Kagi が component/base/assets を依存として使うために継承する必要はない。standalone 解決で確認済み。gpui-base は transitive に入るため crate split だけを理由に全129 production files の import を書き換える必要はない。component は Root、focus traits、theme、Rope 等を再 export している(crates/component/src/lib.rs:98–123)。

    供給元の表現には注意: gpui-pre 0.3.9 は Zed 本家が直接 crates.io に発行した package ではない。registry API は publisher huacnlee、description snapshot of zed@93d7456、公開 2026-10-12、Apache-2.0。platform manifest の zed-rev は 93d745668805029476e6d7fbb19652116fed51de。これは Kit upstream が指定する公開 snapshot を無改造で使う案であり、TomiXRM fork ではない。しかし「Zed 公式 publish のみ」という追加条件ならこの案を満たさない。その供給元判断を ADR-0001/0006 に明記する。根拠: registry API。

    また published gpui-component 0.7.3 と main は同一ではない。download package の .cargo_vcs_info.json は 67265a36e260ffe03c5aaebdcd8d654dbf892eab(#3460)、今回の main は 1188c110…。0.7.3 の依存も gpui-pre =0.3.9 だが、main の新 API・修正が全部含まれると扱ってはならない。本調査の置換案は main 基準、release 化するときは対応版で再照合する。

    #436 の実測移行面

    src/, crates/, vendor/, tests/ の Rust source を対象。広域数値は literal 出現数(コメントと埋込 unit test を含む)であり、すべてを変更する数ではない。下記の builder 等は実際の grep hit を読み、実コードの呼出箇所を区別した。集計は /tmp/kagi-fork-review/migration-counts.json。

    面 現在の量 変更の意味
    gpui_component 参照 production 408 出現 / 129 files、tests 込み 522 / 193 #436 の 52 files は現状ではない。全部 rename は不要
    InputState production 143 / 39 files、tests 込み 190 / 62 全部を Editor にするのも誤り。single-line は残す
    Input::new production-path literal 22 / 17 files(実render builderは18) multiline/editor の constructor と state field を同時変更
    .multi_line(true) 4 / 4 files commit_panel_composer.rs:177、operations/commit.rs:111、pr_mode.rs:989、settings_view.rs:67
    .code_editor(...) 3 / 3 files conflict_view.rs:1093、issues_composer.rs:80、kagi-ui-editor/src/lib.rs:1538
    .auto_grow(...) 2 / 2 files PR comment と Analyze-ignore、加えて commit body を3–12行にできる
    .wrap_row_count() production 1、test 1 commit_panel_composer_render.rs:33 の手動高さ計算と対応 viewport test
    .search_labels(...) 3 / 3 files 各 Editor の constructor。runtime setter と E2E seam も別途変更
    gpui_component_assets 2 / 1 file src/ui/assets.rs:253,262 → gpui_kit_assets
    focused_input() 4 / 3 files window_menu.rs 2、render.rs 1、modal_key_routing.rs 1。後者は既に直接 focus_handle を使う
    production Theme::global_mut 2 / 1 file kagi-ui-core/src/theme.rs:1271,1277。Base への同期が新たに必要
    with_uniform_item_height 7 / 7 files diff_list と6つの非diff list。diffだけの変更ではない
    reset_with_uniform_height 1 / 1 file diff_list.rs:78
    Root selection-group 登録 2 calls / 1 file issue_conversation.rs、clear/group構築は issues_thread.rs にも及ぶ
    直接依存 manifest 7 root、ui-core、ui-ecosystem、ui-editor、ui-file-history、web、vendor terminal

    multiline/editor の field 名(body_input / pr_comment_input / analyze_ignore_input / ConflictEditorInputs / EditorInputs)だけでも20 production files・23 test files に広がる。これは型推論でそのまま通る consumer も含むレビュー集合で、確定の変更ファイル数ではない。Entity<InputState> 等を混在 array/chain にしている箇所は分離必須(commit_panel_composer.rs:56, compact.rs:128, issues_composer_e2e.rs:289,395)。timeline_row.rs:201 は現状同じ Input helper で PR の textarea と Issue の editor を描いており、2つの型別 render entry へ切り分ける。single-line subject/title を multiline に変えるのは不可。

    Commit bodyの注意: 現実装は外側composerが縮められるよう、cached row数からpreferred heightを算出して内側Inputをh_fullにしている。Textarea auto_growだけでこの縮小契約まで同じとは未確認。S6では同じviewport受入を満たす公開layout観測/構成を採るか、Textarea measurement seamを上流へ提案する。Editorに型を変えてgetterだけ得る方法はkey behaviorも変えるのでdrop-inとして推奨しない。

    型移行の具体例:

    // commit_panel.rs:429 / composer.rs:176 / operations/commit.rs:110
    body_input: Option<Entity<TextareaState>>
    let body = cx.new(|cx| {
        TextareaState::new(window, cx)
            .auto_grow(3, 12)
            .placeholder(Msg::CommitBody.t())
    });
    // composer_render.rs:87 -- 上位の手動固定hを外し、Textarea本来のauto-growを使う
    Textarea::new(body).aria_label(Msg::A11yCommitDescription.t())
    
    // conflict_view.rs:1092、issues_composer.rs:79、ui-editor/src/lib.rs:1537
    let editor = cx.new(|cx| {
        EditorState::new(window, cx).language(lang).line_number(true)
    });
    Editor::new(&editor).aria_label(localized_label)
    
    // window_menu.rs:68,132 / render.rs:247
    window.focused_input(cx)
        .is_some_and(|input| input.focus_handle(cx).is_focused(window))
    
    // ui-core/src/theme.rs:1269 の既存palette push直後。もう一方の選択色更新も同期を見直す。
    apply_gpui_component_theme(&k, gpui_component::Theme::global_mut(cx));
    gpui_component::Theme::sync_base(cx);

    上流根拠: base/src/input/base/state.rs:11665,11716,11734,11744、component/src/input/{textarea,editor}.rs、component/src/input/state.rs の AnyInputState、component/src/theme/mod.rs:459–478。Theme::global_mut の public fields を変えるだけでは Base scrollbar/text defaults は古いままになる。この同期漏れは単なる compile 対応では見つからない。Input key context は今も Input(base/src/input/base/state.rs:135)、Editor は Input mode=editor。Kagi の !Input の Git-history shortcut guard を闇雲に改名する必要はない。

    テスト専用 fork API の代替は既に公式 API がある: gpui-kit::test::{TestWindowExt, TestQueryExt} と ElementSnapshot::{role,label,checked,focused,bounds}、gpui_base::TestSupportExt::test_support()。native AX props を読み、追加 layout node を作らない。kit/src/test.rs:28–74、base/src/test_support.rs:16–114、base/src/observe.rs:11–35。これは native VoiceOver の確認や Metal pixel 検査の代わりではない。Kagi の leak detector / actual keyboard dispatch / pixel assertions は残し、単なるテスト用の duplicate state 値で置き換えない。

    見積り: source inspection に基づく確定面は上表。少なくとも7 manifest + assets + typed-input/selection/find/theme/list 各 owner と対応 tests が対象。129 production files 全部を変更する必要はなく、逆に「7 builder だけ」で済む移行でもない。全 workspace を移行して compiler error 数を測ったわけではないため、総変更ファイル数や工数の断定はしない。

    merge可能な slice と受入条件

    「manifestだけ更新して次PRで型エラーを直す」は採用しない。GPUIの型は crate sourceを跨いで互換ではなく、その中間commitはdevを壊す。以下は 旧依存のまま準備を独立してmerge→最後の供給元/型切替は原子的 とする順序。各sliceで build・tests・対象native GUI E2Eがgreenでなければmergeしない。下記は後続実装の受入条件であり、本調査でKagiの検証を実施済みという意味ではない。

    共通ゲート: cargo build、cargo test --workspace(KAGI_LOG_DIR隔離)、関連純粋logic/integration tests、uv run --project ci check-all、format/clippy。GUIは KAGI_GUI_E2E=1 KAGI_GUI_E2E_ONLY='<対象substring>' cargo test -p kagi --features gui-e2e --test gui_e2e_runner -- --nocapture、複数scenarioは必要に応じKEEP_GOING。無filterの全GUI起動はしない。 OS/IME/VoiceOver/fontは対応実機のTier Bを別記録。

    slice scope / devをgreenに保つ境界 依存 受入条件・対象native E2E 主リスク
    S0 供給元方針と事実訂正 本報告の方針、ADR-0001/0006/0222、decisions、Cargoコメント。必要なら現行sourceのまま7 manifestsをworkspace.dependenciesに統一 無し 解決source/lockを変えずbuild/testsがgreen、native smoke bottom_panel。規約文がupstream PRだけでfork増加を許可しない 文書と実挙動の再乖離
    S1 CJK fallback完結・cosmic撤去 terminal config、全glyph shape/metrics経路と不足するCJK pathに明示fallback。cosmic patchだけ削除しlock更新 無し(方針はS0) fontsあり/なしLinuxでterminal/diff/editor/diagnosticの「直・骨・誤」、EN/JA locale、IME、AA、resize、terminal keys/paste。対象 terminal_,diff_,editor_。macOSだけで承認しない Linux default font解決・font未登録・subset coverage
    S2 terminal所有責任の明文化 ADR-0035を継続保守へ改訂。由来/LICENSEとvendor配置を保持し保守責任を明記。名前変更だけでforkが無くなったとは扱わず、不要な移設はしない。機能変更とは分離 無し PTY query response、SGR mouse、selection/copy、Cmd suppression、paste confirmationの既存tests、terminal_ native、一切の削除で安全性を弱めない 名前変更で由来/再配布義務が消えるとの誤認
    S3 会話選択のKagiモデル化 投稿ID+offset、canonical rendered text、copy/selection paintingをKagi-owned rich rendererへ移す。旧pinにもあるGPUI公開text/Element APIで構成しRoot group依存を除く。公式TextViewを薄くwrapするだけでは不可。TextView継続案を選ぶならupstream extension取得とS6のcutoverへ組み込む Kagi-owned renderer案は無し。公式TextView継続案はupstream seam issue_,github_text の該当選択scenario。offscreen中間投稿、reverse drag、before-paint copy、per-post SelectAll、scroll中selection、presentation変更、tab/owner切替、deleted post、見える本文とcopy一致 最も大きいcomponent差分。Markdown view-offset対応、autoscroll、非公開TextView adapterへの依存を残す誤り
    S4 入力chrome・テスト境界の準備 旧pinで使えるsuffix/EntityInputHandler/GPUI公開要素でClear・IME policyをKagi側へ移し、fork専用peek testを描画AX/focus観測へ置換。Find hostの状態/command境界を整理するが、新base Radio/検索engine APIに依存する完成切替はS6に含める 無し(S3と独立) input_names,sidebar_search,settings_controls と現行 editor_find,editor_find_hosts がgreen。EN/JA、IME変換中と通常Escapeの分離、native AX。新APIを旧pinにあると仮定しない Findだけ先に切替えて旧libraryのprivate engineに依存する誤り、focus scope変更
    S5 公式GPUIでリスト契約を成立 小規模のmeasure-all再arm/anchor、大規模geometry owner、6非diff callersも分類して移行。caf50076 patch22件を公式sourceへ戻すのは全callerの切替後 無し(S3のselectionとはinterface共有が必要) diff_,pr_diff_extent,pr_lane,issue_,oplog_restore,inspector_ の該当scenario。initial hint、offscreen splice、resize/zoom、same-count source、thread expansion、通常thumb中間/末尾、oversized tail、focus/reveal、one-shot not-follow。1k/5k/20kの実Kagi計時 geometry/keyboard/selectionの統合。is_scrolled_to_end=Noneをfalseと誤解。公開API routeの実装量
    S6 #436 原子的cutover 7 manifests全てをregistry pre0.3.9+公式Kit mainへ、Input/Textarea/Editor stateと全consumer、Kagi Find host+公式検索engine、base Radioのdot構成、assets名、AnyInputState、theme Base同期、公式test harness feature、profile package名、lock、docsを一括移行。component patch2件削除。過渡的互換shimは残さない S1,S3,S4,S5(S0方針) 全共通ゲート、cargo treeでactive GPUI一種類、patch0。対象GUIをfeature別に分割して実施: commit-panel/undo, editor/find, settings/theme/radio, conversations, lists, terminal, platform-menu。real JA IMEとVoiceOver、Linux/Windows build、web build+既存Playwright specs、native pixel/leak tests 唯一大きな原子commit。型推論で隠れたmulti-state配列、base theme、Rope/selection index・highlighter API差異。新APIを必要とする機能をmanifest-only PRから分離できない

    S6を先にmanifest-onlyでmergeする案、caf50076を[patch.crates-io] gpui-pre*へ載せ替える案、forkのtest-support hooksを消してE2Eを弱める案は採らない。上流へ小さい不具合PRを提案するのは有益だが、投稿すること自体を本調査では行わず、受入待ちを全sliceの必須依存にも置かない。

    継続のacceptanceは「patchが0だから完了」ではなく、既存の安全性と指定UXが保たれること。cross-post selectionを単一投稿copyへ落とす、Radioの丸をcheckへ戻す等はownerが別途仕様変更を承認した場合だけ。vendor terminalを維持するため、zero-patchはzero-derived-codeとは同義ではない。

    方針 — CLAUDE.md / AGENTS.md に入れる不変条件の文案

    以下は提案文であり、本調査ではファイルを編集していない。AGENTS.md を canonical とし、CLAUDE.md は同じ規則への参照を置くか同一内容に揃える。

    公式 upstream 優先・フォーク抑制

    依存 library/crate は、原則として公式 upstream の公開版または公式 repository の固定 commit を無改造で利用する。旧 pin 上に独自修正を追加する前に、現行 upstream の実装・公開 API・既存の利用方法で目的を満たせないかを一次資料で調べる。

    新しい fork、[patch]、依存ソースの vendor copy、および既存 fork への追加 commit は既定で禁止する。必要な場合は owner の明示承認を得て、ADR に①不足する振る舞いと再現、②公式公開 API/更新で代替できない根拠、③変更範囲とライセンス、④upstream Issue/PR(又は出せない理由)、⑤削除条件と追跡 owner を記録する。upstream PR を出しただけでは fork を増やす許可にならない。

    アプリケーション固有の選択・フォーカス・表示 model は Kagi の境界で所有する。ただし依存の巨大な内部実装をコピーして「Kagi-owned」と改名することは、fork 抑制を満たすとは扱わない。製品責務・保守負担・ライセンスを比較して判断する。

    供給元と版は workspace 全体で一致させる。公開 API の有無、修正の upstream 取り込み、crate の publisher、性能値を推測で ADR に書かない。commit URL と実測条件を残す。gpui-pre は Kit 採用の第三者公開 Zed snapshot であり、Zed 公式発行と呼ばない。

    fork 削除時は対応 caller・test・manifest・lock・ADR を同じ green な cutover で移行する。機能を落として patch を消す場合は、事前に owner の明示的な仕様変更承認を得る。テストを削除/弱化して移行完了とはしない。

    dependency 更新の受入は build・unit/integration tests・対象 native GUI E2E。IME/VoiceOver/native appearance/各OSのfont fallbackについて、状態テストを実機確認済みと表現しない。性能は行数・文字列・幅・profile・OS・font・cold/warm/resize 条件とともに記録する。

    既存 docs の具体的訂正対象: ADR-0222 の「推定高さ API がない」「diffだけ opt-in」、docs/decisions.md の同趣旨、Cargo.toml の fork 再追加を勧める旧コメント、ADR-0035 の「upstream が選択/copyを実装したら外す」だけの廃止条件、ADR-0001/0006 の source/pin 方針。cosmic-text patch の削除条件は「upstream release を待つ」ではなく、明示font fallback と対象 Linux の受入確認でも満たせるように改訂する。

    未確認事項・実行証拠の境界

    1. Kagiのmigration実装・build/tests/native GUI E2Eは実行していない。 調査依頼の禁止範囲を守りKagi本体/読み取りcloneを編集していない。zero-patch dependency resolverと独立list実験のみを実行した。S0–S6の受入は将来のmerge条件であり現在のPASSではない。
    2. LinuxのJP glyph・実terminal fallbackは未実機検証。 config/routing/code coverageは確認したが、変更を適用したLinux実機のfont-id/pixels/2-cell alignmentはない。primaryが既に持つCJK glyphや同梱fontが覆わない文字までJPに強制する保証ではない。system CJK無し/SC+JP併存、ja-JP/ja_JP.UTF-8、Regular/Bold、既存session/live updateで検証が必要。
    3. Kagi-owned virtual list / rich selection rendererは設計であり未実装。 公開Element/Scrollbar/selection APIの存在は確認したが、全acceptanceに通る実装が完成したとは主張しない。特にselectionのbefore-paint copy、canonical rendered text、per-post SelectAll、synchronous retirementは落とせない。official TextView as-isを必須にするなら上流extensionが必要。
    4. Textarea auto-growと現在の縮小可能composerの幾何同等性は未実測。 cached getterを消してよいのはcommit_panel_viewportの受入を満たした場合だけ。plain Textareaのmeasurement seamを上流へ提案する代案は残る。
    5. IME Escape capture / Search host / Radio primitive / official AX test API の置換は静的設計。 完全なKagi treeへ適用してcompile/native dispatch/VoiceOverで証明していない。既存testは参考contractであり今回実行済みではない。
    6. 性能はsynthetic realistic unified風CPU drawの実測。 split diff、全てのsyntax span/最大Unicode/異なるfont/backend/DPI、実Kagiのstate更新やGPU presentationは未計測。3回の中央値を普遍的な閾値にしない。現在のupstream mainとregistryで一致を確認したのはlist.rsだけ。
    7. upstream採用見込みは未確定。 Radioの好み判断、selection extension、Textarea measurement、IME1行修正、cosmicPRの採用時期を推測で確約しない。PRを投稿/コメントしていない。調査時点のheadで固定しており、将来mainの内容は別途diffで確認する。
    8. ライセンスはsource/manifestを確認、配布archiveはinspectしていない。 今のKagi bundleがnotice義務を満たすか否かの監査結果ではなく、将来のowner変更時にも保持すべき義務を記載した。GPLは法的に利用不能という説明ではなくKagiの非GPL配布方針との整合判断である。

    今回実際に行った検証

    • current Kagi origin/devとremote devのSHA一致、current公式/fork headとcommit diff/公開API・callsite監査。
    • standalone zero-patch Cargo dependency解決とactive graph確認(公式Kit main+registry pre0.3.9)。
    • 無改造公式pre0.3.9、native CoreTextで1k/5k/20kのdraw timing63件、加えてinitial hint/normal thumb/End/unknown-height/splice/resize-anchorのlifecycle実験。成功ログ artifact://14972。
    • それ以外のKagi build、test、native GUI/browser E2E、Linux描画確認は未実施。過去issue/PR本文の成功記録は今回の実行結果に加算していない。
  3. TomiXRM commented on Oct 14, 2026

    @TomiXRM
    OwnerAuthor

    オーナー判断(2026-10-15)

    • 方針: library / crate の fork は非推奨。公式のものをそのまま使う。fork への新規 commit・pin 変更は禁止(実装 agent に周知済み)。
    • GPUI 供給元: 公式 gpui-kit main が採用する crates.io gpui-pre を許容する → 目標は [patch] ゼロ。
    • 投稿横断選択: 投稿単位の選択 / Copy / Select All に縮小する(ADR-0198 を改訂)。公式 TextView をそのまま使う。

    進め方

  4. TomiXRM commented on Oct 14, 2026

    @TomiXRM
    OwnerAuthor

    #1479 / #436 — fork 解消の確定実装計画(owner 決定反映)

    0. 決定・基準点・この文書の意味

    • 採用する: official gpui-kit main が選んだ crates.io gpui-pre =0.3.9 と同系列 platform/web/wgpu。Kagi の最終状態は 全 manifest の [patch] がゼロ。
    • 廃止する保証: Kagi独自の、Issue/PRの画面外を含む複数投稿選択・Copy・会話全体Select All。投稿単位の選択・Copy・Select All は公式の挙動のまま残す。跨ぎ選択を保証・維持しないだけで、公式がmounted範囲で自然に許す跨ぎ動作は禁止もしない。公式 TextView/default scopeを改変せず使い、ADR-0198を改訂する。旧報告 S3 の独自 rich-selection renderer 案は取り下げる。
    • 調査対象を再取得済み: origin/dev c0a4ea3ab6b3dc6aa68c2fdb2c379a4eefd53f64。本書の Kagi file:line はすべてこのコミット。読取専用調査コピーは /tmp/kagi-fork-review/kagi-plan。元の作業ディレクトリ・worktree は変更していない。
    • 最終依存先の検証済み候補: official longbridge/gpui-component@1188c11003b5cb5b83bc07d3fb9112e7674fbfa7。main を無固定で追わず、component/kit-assets を同じ rev に固定する。gpui-pre は Zed 自身の crates.io リリースではなく、official Kit が採用する snapshot package である、という出自も明記する。
    • 前報: local://fork-review-1479.md。fork 13 commits の全棚卸し、upstream 比較、依存解決証拠、ベンチ生ログは前報を継承。本追補は実装順序・テスト変更契約を確定するもの。
    • 本追補ではビルド・テストを実行していない。以下の gate は実装担当/main が各 merge 前に行う受入条件であって PASS 報告ではない。[INFERENCE] の LOC 見積もりや proposed 閾値を実測値と混同しない。

    証拠リンクの読み方

    Kagi: https://github.com/TomiXRM/kagi/blob/c0a4ea3ab6b3dc6aa68c2fdb2c379a4eefd53f64/<path>#L<line>。
    Kit: https://github.com/longbridge/gpui-component/blob/1188c11003b5cb5b83bc07d3fb9112e7674fbfa7/<path>#L<line>。
    ローカルの全 file:line は上記 SHA に対応する。調査後 dev が進んだら、実装開始時に line mapping と衝突箇所のみ再確認し、既存の新しい変更を上書きしない。

    全 slice 共通の merge 規則

    1. regression は 同じ PR 内で先に書く(必要なら手元で red→green)。失敗するテストだけを dev に merge しない。挙動変更・テスト oracle・ADR/changelog を一つの green merge unit にする。
    2. 準備 slice は現行 pin でビルド可能な public API だけを使う。新しい base Radio / typed Editor / search engine は最終 S6 まで merge しない。S6 は manifest 変更だけ先に入れず、型移行と全 consumer/test を同時に切り替える。
    3. 一度完了した切替に古い API alias・feature flag による旧新両系統・fork 復帰 fallback は残さない。途中の pin は次 slice の prerequisite にすぎない。
    4. merge 前の共通 gate: workspace unit/integration、変更対象 native GUI filter、default と gui-e2e の compile。S6 はこれに Linux/Windows/WASM の実 target compile と web harness を加える。renderer metadata を OS AX/IME/pixel の実測と呼ばない。
    5. native runner は .claude/skills/verify/SKILL.md:49,515–532,590–592 に従う。KAGI_GUI_E2E_ONLY は カンマ区切り部分一致、KEEP_GOING は明示 filter 必須。新 scenario は下記で「新規」と記した名前を登録する。存在しない filter で 0 件 PASS にしない。
    # 実装後に main が行う例。調査中には実行していない。
    KAGI_LOG_DIR="$(mktemp -d)" KAGI_GUI_E2E=1 \
      KAGI_GUI_E2E_KEEP_GOING=1 KAGI_GUI_E2E_ONLY='<slice の filter>' \
      CARGO_TARGET_DIR="$PWD/target" \
      cargo test -p kagi --features gui-e2e --test gui_e2e_runner -- --nocapture
    KAGI_LOG_DIR="$(mktemp -d)" cargo test -j 8 --workspace

    件数/LOC の定義: files_count は下記で列挙する予定変更パス(新規/削除含む)の distinct 数。受入のみで変更しないファイルは数えない。slice 間の重複はあるので合計は unique file 数ではない。LOC は追加+削除の churn(Rust/test/docs/TOML を含む、生成 Cargo.lock の行数は除外)の [INFERENCE] 幅。未ビルドのため最終 S6 の compiler-driven minor fixes は予備幅で明示する。

    1. 追加調査とownerによる仕様解釈の確定

    U0は不要 — mounted cross-viewを禁止するscopeは作らない

    公式のTextViewはwindow-level selection controllerを共有し、mounted複数viewのdrag/Shift-clickを自然に扱う(official component/src/text/window_selection.rs:1703,1858)。独立scopeを作るとComponent Rootのdefault/modal scope管理と競合するが、ownerはその独立scope/境界保証を求めていない。

    したがって per-view selection-boundary APIのupstream PRは作らない、待たない。Kagiのgroup/lease/全post canonical interval/outer edge autoscrollを除去し、公式TextView/default scopeをそのまま採用する。公式に残るmounted cross-viewを失敗とする負条件テストも追加しない。「単一投稿の正のCopy証明」と「Kagi独自の画面外全会話保証を終了すること」を分ける。

    旧報告の独自rich-selection renderer案、追加調査途中のstrict per-post scope案はいずれも撤回する。依存graph上のzero-patchは確認済みであり、selectionに関する外部prerequisiteはない。

    composerの公開API構成は最後に別途確定

    現 commit_panel_composer_render.rs:28–35,79–91 のpreferred3–12行と低windowでのshrink/internal-scrollは引き続き維持する。Textareaのauto_growはleaf min-heightも増やす (base/input/base/element.rs:2518–2524) ので、単にauto_grow+外側min_h0では同じでない。Kagi側の高さ設定+plain Textareaの内部scrollという公開API構成を先に検討し、必要がある場合だけ公式cached-row getterをprerequisiteにする。最終判断と具体的構成はS6に記す。不要なEditorState偽装・別shaper・forkを先に選ばない。

    S0 — 方針と出自を固定する(最初に merge)

    依存: なし。merge unit: 文書・現行コメントのみ。依存更新・API変更なし。

    変更パス(6):

    1. AGENTS.md:13–37: upstream-as-is / zero-patch / owner 承認された範囲変更 / Kagi-owned boundary の不変条件。
    2. docs/adr/0001-gpui.md:21–30: 0.2.2 固定・platform 未公開という歴史記述を保持し、新決定 gpui-pre を追記。
    3. docs/adr/0006-gpui-component.md:19–30,43–44: 0.5.x 留保を supersede、official main 同一 rev / typed API 移行を採用。
    4. Cargo.toml:177–179:「cosmetic polish が欲しければ fork を re-add」の指示を削除。今ある patch をここで早まって削除しない。
    5. docs/decisions.md:25,143–146,279,300–302: 過去決定を書き換えて証拠を消さず、先頭に新しい superseding decision を一件追加。
    6. CHANGELOG.md:6: migration 方針の変更を記録。

    CLAUDE.md は git mode 120000、内容 AGENTS.md の symlink。別内容を複製・上書きしない。物理変更ファイル数は AGENTS 一つで数える。

    不変条件の文案:

    ライブラリは公式 upstream の公開 API を原則そのまま使用する。移行中は #1479 に列挙された既存 pin の削減だけを許し、新しい fork/patch を増やさない。S6 完了後の Kagi dependency graph は [patch] を持たず、gpui 系は official gpui-kit が採用する gpui-pre の同一 package identity に統一する。足りない要件はまず Kagi 所有の domain/UI 境界で解決し、必要なら upstream 提案を行う。依存ライブラリの fork・ソースコピー・隠し互換 shim を便宜的に再導入しない。仕様縮小は owner の明示決定・ADR・consumer oracle の同時更新を要する。Kagi-owned terminal は出自と MIT/Apache notices を保持する例外的な所有コードであり、他ライブラリの fork を正当化しない。

    regression/gate: docs のリンク・CLAUDE symlink維持、Cargo の実依存内容が不変。共通 gate の既存 baselineを利用し、新しい native scenario は不要。LOC: 90–160。

    S1 — 明示 fallback を全 native shaping 経路に通して cosmic patch を除去

    依存: S0、S2(terminal 所有方針を先に確定)。merge unit: fallback plumbing+テスト+cosmic patch/lock除去を一緒に merge。component / Zed pin はまだ現状維持。

    production の変更位置

    Kagi file:line 変更
    vendor/gpui-terminal/src/view.rs:141,170,505,1208–1220 TerminalConfig に font_fallbacks: Option<gpui::FontFallbacks>(既定 None)、renderer作成・live config変更へ渡す。Kagi caller が明示するので汎用既定を勝手に Noto にしない。
    vendor/gpui-terminal/src/render.rs:170,213,247,633 renderer に fallback を保持。ASCII M の metrics と実 glyph 描画の 両方の Font に設定。Arc-backed 値の clone を使い、各 glyph ごとの Vec 構築なし。
    src/ui/terminal.rs:111–132 唯一の production build_terminal_config が kagi_ui_core::fonts::ui_font().fallbacks を入れる。539–551,563–620 の初期font選択と既存session更新経路をそのまま通す。
    src/ui/commands.rs:2082–2114 既存 all-session live update の 受入確認箇所。新規の第二更新ルートは作らない(基本変更不要)。
    src/ui/render_status.rs:87–103(caller 181,191,335) Fresh Font に Root の fallback が継承されるという誤解を除去し ui_font() 由来の shaping と描画を一致させる。
    vendor/gpui-terminal/src/view.rs:113 / src/lib.rs:60,147 / src/main.rs:130 / README.md:48 全 struct literal/doc/example を新 field に追随。renderのconstructor例 render.rs:54,211,725,768 も同時変更。
    Cargo.toml:171–176 / Cargo.lock cosmic fork patchのみ除去、通常 registry 0.19 系へ解決。不要になった空 [patch.crates-io] も残さない。
    docs/adr/0130-bundled-japanese-font-fallback.md:29–31 「implicit pathを保護するため patchを残す」を明示fallback完了により supersede。static Regular/Bold と OFL は維持。

    regression を先に置く

    • crates/kagi-ui-core/src/fonts.rs:28,40,58,87–137 の既存実 Cos­micTextSystem テストを拡張。ABC日本語直骨誤、Normal/Bold、UI Inter / terminal JetBrains Mono / 実在する Nerd 系 primary、ASCII と2-cell CJKを対象にする。run が2個あるだけでは不十分。同じ text system で primary を明示 Noto Sans JP にした参照 shape の日本語 run FontId と一致、ASCII run は primary、Regular/Bold は対応する別 static face と照合する。非公開 TextSystem::font_id を呼ぶ設計にはしない。
    • vendor/gpui-terminal/src/render.rs:724–779 付近の renderer unit と view.rs config update unit: clone/new/live update の fallback伝搬、font変更でmetrics再計算、null/defaultの汎用契約を確認。
    • 新規 tests/recovery/terminal_fonts.rs: terminal_font_fallback 登録。実terminalのfont切替・zoom・セッション切替・再起動で同じconfigが伝わること、ANSI bold / CJK2セル / ASCIIカーソル位置を確認。PTY に日本語を送れたことだけを glyph保証としない。
    • 計測側も production と同じfallbackにする: tests/recovery/status_bar_fit.rs:202, time_locale.rs:112, density_rest.rs:97–120, finder_scale_editor.rs:88–95, finder_scale_history.rs:78–85, finder_scale_rest.rs:137–141, home_heading.rs:65–73, inspector_scale.rs:91–98。Window.text_style 外で Fontを作るprobeは Rootを継承しない。

    Linux の必須受入(macOS PASS で代替不可)

    1. Linux runner上で cargo test -p kagi-ui-core linux_cosmic_text_uses_bundled_fallback_for_regular_and_bold と新しい同系font単体テスト。既存 new_without_system_fonts を使い、bundled facesのみで ASCII/日本語が正しい face にshapeされることを検証。
    2. 別の隔離 Linux native run(X11、Waylandはそれぞれ対応環境で)。ja_JP.UTF-8 と en_US.UTF-8 の新規プロセス、system Noto SC/JP がある環境と最小font環境の両方。個人PCのfont削除/グローバルfontconfig変更は禁止。用意したVM/containerのprivate fontconfig/cacheで行う。
    3. terminal に ABC 日本語 直骨誤 を通常/ANSI boldで出し、同じwindowに明示 Noto Sans JP の参照描画を並べたテストfixtureまたは隔離 captureで比較する。Regular/Bold・glyph形・2-cell位置・selected font と fallback の役割を残す。native画像はpid指定captureを使う。Inter根の一般ラベル、status fitting、commit/Issue Input、TextViewでも日本語が同じfallbackになることを確認。
    4. 既存 tests/gui_e2e_runner.rs は macOS gated moduleが多い(例 48,872–891)。このrunnerをLinuxで空実行してPASSとせず、Linux実shape unit+native実機/Tier Bを独立証拠にする。S6後の pre移行でも同じLinux gateを再実行。
    5. 未収録の全Unicodeや、primary font自体が持つSC glyphの強制置換は保証しない。明示fallbackはprimaryにglyphがない場合に効く。Nerdのicon優先順位を壊さない。

    native filters(既存+新規): terminal_,status_bar_,relative_time_ja,cleanup_local_date,oplog_row_typography,issues_row_secondary_text,finder_scale_rest,home_heading,inspector_scale。finder_scale_rest が editor/history のinspectを内部実行する(同file:113–114)。terminal_font_fallback は新規登録を必須にする。terminal paste / control / focus / auto-lock の既存oraclesは変更しない。

    変更パス24: 上表のroot2、vendor5、production2、fonts1、計測test8、新nativetest1、runner1、ADR-0130/decisions/CHANGELOG/verify skill4。LOC: 550–950(lock再解決を除く)。リスク: font fallback fieldを初期化時だけ渡してlive設定を落とすこと、実測probeと描画fontの乖離、Linuxで偽の空suite。

    S2 — terminal を Kagi-owned と明文化(S1 より先に merge 可)

    依存: S0。merge unit: 所有/出自/noticeの説明のみ。terminal実装・公開挙動は変えない。

    変更パス(6):

    • docs/adr/0035-vendor-gpui-terminal.md:1,14–30:「temporary fork」「upstreamにselectionが来たら即削除」を撤回。terminal frontend/selection/PTy統合はKagi所有、VTEは引き続きofficial alacritty_terminal、upstream出自は追跡する。
    • vendor/gpui-terminal/README.md:1–3,60–67: upstream一般説明とKagi変更を区別し、既に実装済みのselection/scrollbackを「未実装」とする古いlimitationを除く。
    • vendor/gpui-terminal/Cargo.toml:1–10,23–26: normalized registry copyのまま「自動生成で編集しない」という誤説明を修正し、Kagi-owned packageの説明とupstream originを両方明示。名前の変更・directory移動はしない。元authorの出自は消さない。
    • Cargo.toml:51: vendored forkという一時性コメントをKagi-owned/ADR参照に更新。
    • docs/decisions.md, CHANGELOG.md。

    license: LICENSE-MIT / LICENSE-APACHE と attribution をそのまま保持。MIT OR Apache-2.0のupstream由来をKagi著作だけに置き換えない。Zed terminal(GPL)への転写・丸ごと置換は採らない。機械的 rename とemulator rewriteは本計画のscope外。

    regression/gate: code diffがないこと、packagingにnoticeが残ること。既存 terminal_paste_bracketed,terminal_paste_control,terminal_focus_exit,terminal_hide_focus,terminal_states,terminal_auto_lock を隣接baselineとする。LOC: 110–190。リスク:「owned」を理由にupstream noticesを消すこと、単なるdirectory renameをfork解消と誤認すること。

    S3 — 投稿単位の正条件を固める準備(旧 S3 を置換)

    依存: S0。merge unit: 現forkでも満たせる単一投稿の正条件、mounted/unmounted witnessのみ。実際のgroup除去・公式挙動へのoracle切替・ADR現仕様改訂は S6の原子的cutoverに統合する。これにより旧pinの非managed default selectionと新official controllerを二度実装・検証する寄り道を避ける。

    変更パス4: tests/recovery/issue_conversation_selection.rs, tests/recovery/issue_offscreen_select_all.rs, tests/recovery/issue_conversation_support.rs, CHANGELOG.md。

    • selection.rs:67–114: 巨大単一postのexact Unicode/rendered-text Copyを明確にする。
    • selection.rs:116–195: reorder/prepend後に選択postが本当にmountedであるwitnessを追加。outer-unmountでもretainする旧要求と混同しない。
    • offscreen_select_all.rs:76–162: unmount前のmounted partial→Cmd-A/fullの正条件を追加。旧offscreen oracleの改訂はS6まで待つ。
    • support :168–177,223–238: 新frameのpaint bounds witnessを使う既存physical drag/poison clipboard helperを再利用。graph_focusもimportするのでmoduleごと消さない。

    gate filters: issue_conversation_giant_copy,issue_conversation_accepted_identity,issue_conversation_offscreen_select_all,graph_copy_foreground。LOC: 250–400。準備テストだけで仕様変更完了と呼ばない。

    S6で切り替える既存13 scenarioの全oracle

    登録 tests/gui_e2e_runner.rs:4089–4138。公式の偶然のmounted cross-viewを禁止するassertは設けない。新方針で不要な旧保証を、明示的に意味を変更した単一投稿testへ置換する。

    既存scenario / 登録line owner決定後のoracle
    issue_conversation_offscreen_copy /4089 (selection.rs:27–65) A→Z/逆順・未paint Mを含むALPHABET全文保証を終了。A内のpartial/fullを確立→Copy exact A、別のZをfresh選択→exact Zという単一投稿正条件へ。A選択後outer rowを実unmountしたframeでは旧Aのselection authorityを保持しない。複数mounted post間dragの結果をKagiの固定期待値にしない。
    issue_conversation_giant_copy /4093 (:67–114) 保持: 一つのpostのGIANT_000〜179 exact全文、same-update move→Copy、末尾/Reply到達。.scrollable(false)なので内部block virtualizationの証明ではない。
    issue_conversation_accepted_identity /4097 (:116–195) mounted Bは同author/time reorder/prependでもOLD_B保持。accepted OLD_B→NEW_B/deletionはbefore-paint POISON、fresh選択でNEW_B。reorderで実unmountした場合は公式clearに従う。
    issue_conversation_refresh_anchor /4101 (:197–255) 保持: unchanged refresh/前方insert-deleteのreading PostKey+intra-y、失敗時accepted mounted M Copy/anchor/error chrome。
    issue_conversation_geometry /4105 (:256–286) 保持: exact Unicode/U+2009 code literal、sidebar幅/zoom/theme変更後mounted partial Copyとfresh drag。scopeを独自activateしない。
    issue_conversation_edge_drag /4109 (:288–372) outer conversation edge-scroll保証を終了。旧positive全会話traversalを除去し、single-post gesture終了/departure後にKagiの残留outer taskがscrollしない回帰へ置換。公式がmounted間でどこまで選ぶかを禁止しない。内部scrollのpositiveは別single-post test。
    issue_conversation_delayed_rejected /4113 (lifecycle.rs:124) 保持: refresh中accepted A、superseded completion拒否、repo retarget→同update CopyがPOISON、late A復活なし。
    issue_conversation_return_pending /4117 (lifecycle.rs:50) 保持: 旧pending readが再開generationへsettleしない、Loading/Reply→新accepted、fresh選択。
    issue_conversation_return_failed /4121 (lifecycle.rs:89) 保持: unaccepted失敗→departure→returnのLoading/Error/Reply、ghost body無し。
    issue_conversation_two_owners /4125 (lifecycle.rs:199) 保持: 実session/repo B、tabswitch/ownerclose before-paint POISON、background accepted AがBで権限を持たない。同issue番号でもowner別。
    issue_conversation_copy_priority /4129 (lifecycle.rs:314) 保持: Reply Input全選択/Copy優先、modal表示でbase即withdraw、modal閉じても復活なし、mode/Home離脱即POISON。
    issue_conversation_enabled_input_copy /4133 (issue_enabled_input_copy.rs:75–164) setupのbody+comment全文をbodyだけのexact Unicodeに変更。Input draft全選択/空Input Copy consumes/POISONは保持。programmatic Input focus→physical Tabで元mounted postの選択へofficial fallback。クリックInputによる公式selection-clearとは別witnessにする。
    issue_conversation_offscreen_select_all /4137 (issue_offscreen_select_all.rs:76–162) outer-unmount後PARTIAL維持→Cmd-Aでpost全文のKagi独自fallbackを撤去。実unmount後Copy/Cmd-A/Cは旧postを復活させない。単に戻しただけでは旧selection復活なし。fresh physical focus→Cmd-A/Cは元のexact full rendered GFM/Unicode。draft/producer/repo不変条件は維持。

    画面外単一投稿の全文取得は選択controllerで復元しない。後述の調査のとおり、既存の投稿本文Copyボタンも存在しない。目的の投稿を再表示して公式操作する方針であり、会話全体Copyの代替機能は新設しない。

    S6で追加する実native scenario(新規登録)

    tests/recovery/conversation_official_selection.rs(新規):

    • issue_conversation_post_select_all: mounted body/comment各単独のpartial/full、rendered Unicode/GFM/コードとprivacy、公式Copy/SelectAll。
    • pr_conversation_post_select_all: description/review/commentの単独Copy、同number別repo/session、accepted source差替え。既存 pr_same_number/pr_threads はclipboardを検証していないので代用不可。
    • pr_thread_post_select_all: inline thread comment単独のpositive。既存full-hunk Copy Buttonは別契約のまま。
    • issue_conversation_official_rerender: mounted partialを保った実Root再render・chrome更新・theme/width/zoom。default scope維持を確認。
    • issue_conversation_giant_internal_selection: bounded parent + official .scrollable(true) のテスト用実wrapperで同TextViewがmountedのままinternal blockを仮想化し、single-post全文Copy/内部edge task停止。productionを勝手にnested-scroll化しない。

    unmounted focus: 公式のactual-unmount cleanup (base/text_selection.rs:1294–1323, base/text/selection_adapter.rs:147–158) を採用。retained Entityを理由に旧postへSelectAllを転送しない。Kagi既存のfocus回復経路を使い、隣post/Replyへ自動SelectAllしない。Input/modalが既にfocusを持つ場合は触らない。独自scope/presence registry/全post走査を新設しない。

    source replacement/departure: official set_textはprefix追加をstream appendとしてselectionを保持する (base/text/state.rs:315–335)。accepted snapshot差替えはstreamではないので、selected旧sourceを明示clearしてから入れ替える。source/deletionはcxを持つaccepted mutationで同期clearする。session/modal/Home離脱は既存Kagiのactive/generation guardをCopy/SelectAllのcaptureで検証する(詳細S6)。任意のno-App DropがSDK内部stateまで即時clearするという旧lease固有保証は維持しない。

    S4 — 旧 pin 上で Clear / IME / Input AX oracle の依存を外す

    依存: S0。S3と共有するrootファイルはrebaseで統合。merge unit: 旧pinで動くpublic Input suffix・GPUI action境界・実native AX inspectionのみ。Find engine / Radio / typed statesはここに混ぜない。

    対象と実装契約

    • src/ui/sidebar_panes.rs:500–519 の唯一の clear_button_aria_label を削除。Input::new(...).aria_label(...).cleanable(false) とpublic suffixに既存Kagiのnamed Buttonを置く。空ならsuffix無し。押下は同じInputの clean(window,cx) と focus復帰。表示size/hitbox/tab順・locale・query ownershipはそのまま。別TextInputや名前付けだけのGroupを増やさない。
    • src/ui/sidebar_focus.rs:195–218 の「非empty Escapeはclear+Input維持、empty Escapeはlistへ」を受入契約として維持(原則変更不要)。IME中のEscapeがそれを先に実行しないようにする。
    • 共有公開API helperを新規 crates/kagi-ui-core/src/input_actions.rs、exportを src/lib.rs:21 付近に置く。WindowExt::focused_input と EntityInputHandler::marked_text_range による action開始時 のIME状態観測を行う。
    • Kagi root (src/ui/render.rs:690–703, state src/ui/mod.rs) と独立Settings root (src/ui/settings_window.rs:316–372) の既存containerに input::Escape のcapture/bubble境界を設置。二段式: captureで現在のfocused Inputのmarked有無をwindow-ownerのboolに上書きして cx.propagate()、kit childが通常のmenu/multicursor/inline completion/touch selection/IME順で処理、bubbleまで来て開始時markedならそこでconsume、そうでなければpropagate。次のcaptureで必ずresetする。renderごとのRc/Vec allocationやglobal別selection registryを作らない。
    • これにより upstream base/src/input/base/state.rs:2176–2213 のmenu→cursor→completion→touch→IMEという優先順位を飛ばさず、IME unmark後に別のEscape binding(sidebar clear / modal close)へ落ちることを止める。単純なroot早期unmarkはその優先順位を壊すので採らない。
    • 現行Kagiには .clean_on_escape() callerがないことをgrep確認済み。将来同設定を導入する場合、childがclearする前に止める必要が生じるのでこの契約も同時改訂する。本sliceは勝手にclean_on_escapeを有効化しない。
    • S6ではhelperのfocused stateをpublic AnyInputState::{Input,Textarea,Editor} matchへ変えるだけ。型消去のために全入力を旧InputStateへ戻すshimは作らない。

    tests first

    1. tests/recovery/sidebar_search.rs:119–146 / sidebar_filter.rs:33,124 / input_names.rs:10,30–38,66,100,112 の take_rendered_inputs_for_test / rendered_aria_label を 実AX tree oracleへ移す。tests/recovery/native_ax.rs:21–38 の既存 Window::debug_a11y_tree_json を再利用。tree["gpui_focus"] が指す実 nodes[id]["aria"]["role"/"label"] を見る既存先例は blame_toggle.rs:23–26,51–55。外側Group名や宣言定数をassertして終わらせない。
    2. Clear: emptyで実Button不存在、EN/JA別名、AppKit Pressとpointer双方でqueryのみ消える、TextInput名は不変、focus保持。IME composition→1回Escapeで未確定状態だけ終了→次Escapeでquery clear→さらにEscapeでlist exit。
    3. 新規 scenario input_ime_escape_boundaries を既存 tests/recovery/input_names.rs 内に追加しrunner登録。Sidebar・Commit body・Issue editor・独立Settings textareaで、IME中のEscapeが祖先のclose/clearに届かず、通常Escapeの既存順序が生きることを検証。menu/completionが開いている場合はその優先消費を保持。button focus時はhidden editorにediting actionを送らない。
    4. editor_find の fork SearchPanelProbe は まだ削除しない。新しいsearch engineがない旧pinで代用品を先行投入すると二重実装になる。S6で実hostとoracleを同時置換する。

    変更パス15: core新helper/lib、root mod/render/settings_window/sidebar_panes、input_names/sidebar_filter/sidebar_search/native_ax、runner、ADR-0014/decisions/CHANGELOG/verify skill。

    gate filters: sidebar_search_,sidebar_filter_tab_entry,input_names_inventory,input_ime_escape_boundaries,settings_,commit_panel_body_focus,compact_rail_cmd_f(新規名を登録したことをrunner summaryで確認)。LOC: 450–750。リスク: captureだけでkitのpopup優先を潰すこと、前eventのmarked flag再利用、native TextInputではなく名前付きwrapperを数えること。

    S5 — zed list fork を不要にする(4つのgreen merge unit)

    現snapshotの実検索: with_uniform_item_height 7 callsites / 7 files、reset_with_uniform_height 1、measure_all 0。7 listsという意味ではない。shared diffは6 retained owners、restore helperは2 panesを支える。

    geometry所有を確定

    現callsite 採用するofficial/public-API側の方式
    core/diff_list.rs:71,78 → Main/History/PR/Conflict/Editor WIP/Editor History Kagi-owned 一つの共有 variable-list Element/handle。code wrap/split cell/overlayは固定行ではない。既存row renderer/selection/approvalを再実装しない。
    src/ui/pr_lane.rs:237 official uniform_list。elision/commitとも .h(scaled ROW_H) (373,465)。
    src/ui/inspector_files.rs:140 official uniform_list。dir/More/fold/fileは同slot (284,299,335,396)。Unavailable (307–312)にも明示同height slotを与える。
    src/ui/operations/oplog_restore_inspection.rs:82 refs/AFTER graph双方official uniform_list。refs oplog_restore_card.rs:122–123,393–420、preview oplog_restore_preview.rs:270–370。
    src/ui/issue_conversation.rs:104–105 accepted postsは同じKagi variable owner、既存PostKey/acceptance/generationを再利用。
    src/ui/issues_mode/feed.rs:186 同variable owner。:46はmin_hにすぎずtitleは:146–153でwrapするのでuniform不可。
    src/ui/tab_view.rs:596–601 pending thread 同variable ownerに統一。2 outer rowsでもcomposer previewは無制限rich text (issues_composer.rs:412–424)。小list用の第二geometry strategyを作らない。

    measure_all の採用閾値と実測を混同しない

    本実装のproduction採用閾値は0(使用しない)と確定。理由は「全件計測で解決できないからゼロ」ではなく、実測で1k coldがすでに1frameを越え、唯一の小候補pendingも巨大previewを許すため、既に必要な共有variable ownerを使う方が単純だから。count-only threshold / 大list scroll_to_endだけ、という案は採らない。

    実験上の安全上限を論じるなら ≤256 rows かつ total64KiB / row8KiB / total1024 projected wraps / row64 wraps、画像・table等cost不明なら不採用が保守的な評価候補。しかし256の測定は未実施であり、この条件分岐・feature flag・未実装budget modeを製品に入れない。将来使う判断をするならnative actual-row cold/resize/reset/splice p95≤8ms、warm≤1ms、全frame≤16.7msを先に測る。今回のmergeはこの将来評価を前提にしない。

    既に実行したofficial gpui-pre0.3.9のCPU draw測定(再実行なし、artifact://14972):

    rows cold median ms warm resize 1000→600 reset
    1,000 17.656 0.717 16.132 15.413
    5,000 87.998 2.679 83.335 77.996
    20,000 399.904 13.776 345.734 335.158

    M1 Pro/CoreText/release、EN/JA diff風rowとhunk/review card、各3fresh context。GPU present/実Kagi E2E/256rowsの測定ではない。measure_all後のoffscreen spliceは再arm必要、automatic resizeだけではfraction anchor非保持、scroll_to_end成功でも未知heightがあればis_scrolled_to_end=Noneというlifecycle実測も前報どおり。

    共有variable ownerの必須契約(fork source copyではない)

    crates/kagi-ui-core/src/variable_list.rs(新規)を既存pane/tabの寿命で保持。既存 DiffListLayout はsource/split/gutter identity、RowScroll/RowFocusはnavigation、既存generic with_vertical_scrollbar (render_helpers.rs:791–806) はpublic ScrollbarHandleを再利用。別のfocus/selection managerは作らない。

    • source-generation単位のstable row keys、numeric measured/estimated heightsとprefix index、viewport/offset、reading key+intra-row fraction、pending reveal/one-shot End、thumb drag-start extentを一つのownerで管理。visible+overdrawだけElementを構築。全text clone/全row shapingをrenderごとにしない。
    • source替えはreset、同source recolor/同selection/unchanged reloadはmeasured cacheを保持。width/rem/font/line-height/split/gutterはmetric generationを変える。
    • 同count/offscreen insert/replace/deleteでもaffected heightsをinvalid化。new slotは非zero estimateを持つ。変化前のanchor fractionをcapturedし、変化後のactual row heightへ解決。消えるkeyは最寄りsurvivorへ一貫して移す。
    • true Endは実最終itemからviewportを充たすまで測る。oversized tailはbottomを合わせる。end intentは解決後consumeし、appendで勝手にTail/followを始めない。
    • thumb drag中はpointer mappingのextentを固定、interiorはprefix inverse、thumb終端はtrue End intent。単にoffsetをestimated maxへclampするだけでは不可。
    • actual official TextViewのfit-content giant postはその一rowの全量layout costを持つ。outer virtualizationだけで巨大単一postのframe budgetを保証しない。全文/Reply到達は維持し、巨大row測定は通常row性能と分けて報告する。

    shared diffの全callers(取り残し禁止)

    • source sync core/diff_list.rs:47–81、constructor src/ui/render_helpers.rs:540–541、sync 606–612、list 674–721、keyboard mapping 725–750。
    • Main Diff src/ui/main_diff_pane.rs:42–43,57–58,69–94,204–214。
    • File History src/ui/file_history.rs:34–35,62–77,174–175。
    • PR diff src/ui/pr_mode.rs:71–72,332–333,1765–1781、PR conflict 101–102,345–346,578–588,1741–1745。
    • Editor WIP crates/kagi-ui-editor/src/lib.rs:539–540,671–672 / src/ui/editor_workspace.rs:62–70。
    • Editor History crates/kagi-ui-editor/src/lib.rs:466–467,648–649,3394–3395 / src/ui/editor_workspace.rs:86–94。
    • test fixture src/ui/github_pr_detail_tests.rs:35–36,46–47。
    • selection navigation src/ui/diff_selection/keyboard.rs:79,165–200、overlay splice render_helpers/row_overlay.rs:86–104、thread source invalidation pr_mode/threads.rs:49–70,330–337。
    • existing RowScroll crates/kagi-ui-core/src/keyboard_rows.rs:43–109 にOwned variantを追加する。無関係なnative List variantは残す。

    S5-A — 既存keyboard adapterとPR lane(8 files / 250–450 LOC)

    ListKeyboard::rows/focus_first (src/ui/list_keyboard.rs:19–55) をRowScroll引数へ一度で切替。全caller branch_picker.rs:49,62, home_recent.rs:48, pr_lane.rs:238, oplog_restore_inspection.rs:83, issues_mode/feed.rs:75 を同mergeで変更し、旧overloadは残さない。PR lane owner/new pr_lane.rs:38,46、reset 230–231、render 289 をuniformへ。現在描かれたrangeは既存RowList::as_drawn方式で報告する。

    tests first: tests/recovery/pr_lane_keys.rs:41,130,210 にtail/Home/End/Page・window-key変更・zoom・実thumb extentを追加。branch/Homeはargument adapterだけでリスト方式は変えない。

    S5-B — Inspector / restore uniform(10 files / 450–800 LOC)

    依存A。Inspector owner/new/reset/splice inspector_files.rs:62–95、More末尾navigation 142–145、render/scrollbar 159,182–187 を変更。既存generated-fold/file pathによるsurvivor focusを保持。restore owner/geometry/wheel/parent reveal oplog_restore_inspection.rs:60–143,157–158、native専用E2E返値 193–200 をuniformへ。

    tests first: inspector_history_keyboard.rs:63,153,384,388, inspector_file_rows.rs:152, oplog_restore_keys.rs:16,232,304–310,553,577。scroll可能paneは端へ達するeventを自分がconsumeし、次のoutward wheelだけ親へ渡す。no-overflow paneは親へchain。parent reveal後のpointer scrollを引き戻さない。Unavailable/More含む全slotに同height。ADR0214 342–355 のListState固有説明を更新。

    S5-C — variable owner + 6 diff owner全切替(22 files / 2,250–3,700 LOC)

    依存S0(A/Bとは独立、core keyboard_rows共有編集は順に統合)。new moduleだけを未使用scaffoldとしてmergeせず、全6consumerと同時導入。

    tests first:

    • core/diff_list.rs:106,132 の同count別source/repaint/gutter/expansionの回帰を移植。row_overlay.rs:158,180,189 のbase-row対応/countテストは残す。
    • new module内unit: prefix inverse、offscreen same-count replacement、splice/delete前方、width fraction、font/zoom/gutter generation、frozen drag extent、one-shot trueEnd/no-follow、oversized tail/source-drop。
    • 既存7 native oracleは全て維持: pr_diff_extent_unified (runner3128), split3132, reflow3136, threads3140, source_recolor3144, same_selection3148, editor_diff_extent_unchanged_reload3152。
    • test observer pr_diff_extent_same_selection.rs:77,189–260, editor_diff_extent_reload.rs:243–272 をlive owned measured-height+実paint boundsへ変更。estimateを測定済heightと偽って返すListState互換testshimは禁止。
    • 実thumb interior/end、offscreen review card更新のlegを既存extent filesに追加。20,005 rows、single large wheelでtail、逆wheelでfirst、oversized tail bottom≤1px、source recolor/reclick/reloadでdistant measurement保持を弱めない。

    S5-D — Issue variable geometry統一(12 files / 800–1,400 LOC)

    依存A,C。accepted conversation issue_conversation.rs:76,104–105,166,217,273–350、thread render issues_thread.rs:87,127,165–207、feed owner tab_view.rs:439,582 / pending owner 463,596–601、feed splice/paging issues_mode/feed.rs:177–234 を同ownerへ。

    現pinでgreenを保つため重要: S3は準備テストだけなので、この時点では既存forkのgroup/viewport callbackを、新geometry handleへ必要最小限に接続して現仕様を保持する。これを新APIとして増設せず、S6で既存groupごと削除する。S5-D単独でcross-postの負条件を有効化しない。最終selector/rendererを二重実装することとは違い、現行consumerのgeometry交換である。

    tests first: issues_pagination.rs:60,109–111,133,150,176,263,281、conversation selection/lifecycle、github_text.rs:135,181。same-count offscreen post/metadata/locale変更、reading PostKey前のinsert/delete、巨大composer preview、最終post/Reply到達、width/zoom、append時no-follow、filter中の無用paginationなし、pending→acceptedのfocus/scroll ownerを追加。

    opaque reset callers github_issue_state.rs:52,90,307, github_issues.rs:206,236, issues_composer_e2e.rs:109,563 は元のexplicit reset意味を維持できれば文字変更不要。reset-to-startとappendを混同しない。per-post semantic cutoverはS6で行う。

    S5 各merge unitの全変更ファイル・filters

    S5-A — 固定PR laneと既存RowScroll adapter

    依存: S0。files_count 8。

    • src/ui/list_keyboard.rs
    • src/ui/branch_picker.rs
    • src/ui/home_recent.rs
    • src/ui/issues_mode/feed.rs
    • src/ui/operations/oplog_restore_inspection.rs
    • src/ui/pr_lane.rs
    • tests/recovery/pr_lane_keys.rs
    • CHANGELOG.md

    gate filters: pr_lane_keyboard,pr_lane_selected,pr_lane_count,home_recent_rows_keyboard,branch_picker_screen_departure。

    S5-B — Inspector・restoreのuniform化

    依存: S5-A。files_count 10。

    • crates/kagi-ui-core/src/keyboard_rows.rs
    • src/ui/inspector_files.rs
    • src/ui/operations/oplog_restore_inspection.rs
    • src/ui/operations/oplog_restore_card.rs
    • src/ui/operations/oplog_restore_preview.rs
    • tests/recovery/inspector_history_keyboard.rs
    • tests/recovery/inspector_file_rows.rs
    • tests/recovery/oplog_restore_keys.rs
    • docs/adr/0214-oplog-panel-and-point-restore.md
    • CHANGELOG.md

    gate filters: inspector_files_keyboard,file_history_focus_return,inspector_generated_focus_retained,inspector_overflow_keyboard,inspector_file_row_hit,inspector_scale,oplog_restore_。

    S5-C — 共有variable geometryと全diff owner切替

    依存: S0。files_count 22。

    • crates/kagi-ui-core/src/variable_list.rs(新規、実consumerと同時)
    • crates/kagi-ui-core/src/lib.rs
    • crates/kagi-ui-core/src/diff_list.rs
    • crates/kagi-ui-core/src/keyboard_rows.rs
    • crates/kagi-ui-editor/src/lib.rs
    • src/ui/render_helpers.rs
    • src/ui/render_helpers/row_overlay.rs
    • src/ui/main_diff_pane.rs
    • src/ui/file_history.rs
    • src/ui/editor_workspace.rs
    • src/ui/pr_mode.rs
    • src/ui/pr_mode/threads.rs
    • src/ui/diff_selection/keyboard.rs
    • src/ui/github_pr_detail_tests.rs
    • tests/recovery/pr_diff_extent.rs
    • tests/recovery/pr_diff_extent_invalidation.rs
    • tests/recovery/pr_diff_extent_same_selection.rs
    • tests/recovery/editor_diff_extent_reload.rs
    • tests/recovery/pr_detail_adaptive.rs
    • docs/adr/0222-diff-list-estimated-height.md
    • docs/adr/0209-pr-review-threads-on-diff.md
    • CHANGELOG.md

    gate filters: pr_diff_extent_,editor_diff_extent_unchanged_reload,diff_keyboard_selection,split_diff_keyboard_order,diff_selection_revision,diff_hunk_buttons,pr_threads,review_hunk_full,pr_detail_adaptive,pr_detail_lifecycle。

    S5-D — Issue feed・accepted/pending conversation geometry

    依存: S5-A, S5-C。files_count 12。

    • crates/kagi-ui-core/src/variable_list.rs
    • crates/kagi-ui-core/src/keyboard_rows.rs
    • src/ui/tab_view.rs
    • src/ui/issue_conversation.rs
    • src/ui/issues_thread.rs
    • src/ui/issues_mode/feed.rs
    • tests/recovery/issues_pagination.rs
    • tests/recovery/issue_conversation_selection.rs
    • tests/recovery/issue_conversation_lifecycle.rs
    • tests/recovery/github_text.rs
    • docs/adr/0198-read-only-issues-workspace.md
    • CHANGELOG.md

    gate filters: issues_pagination,issues_row_secondary_text,issue_conversation_,issue_body_zoom,review_hunk_full。

    Zed patch除去はS5単独unitにしない。最後のS6でroot Cargo.toml:181–205(22 entriesは184–205)とlockをofficial package familyへ同時変更する。古いline番号181–202という前snapshot由来の値は使わない。S5A–Dのchurn計は [INFERENCE] 3,750–6,350 LOC、同一ファイル重複あり。

    投稿 Copy ボタンの調査結果 — 「既存ボタンで代替」はできない

    現c0a4ea3には、Issue/PR投稿本文全体のCopyボタンがない。 UI全体のCopy関連検索に加え、実際の全投稿rendererを確認した。

    投稿 一次根拠 結果
    Issue本文/コメント src/ui/issues_thread.rs:214–276、processor 93–112 meta/title/TextView/state/avatarのみ。投稿Copy無し。
    PR description src/ui/pr_conversation.rs:25–99、本文 82–89 投稿Copy無し。
    PR review/comment/line-comment src/ui/pr_conversation.rs:228–334,509–529 本文Copy無し。diff_hunkのあるreview commentだけ次行の別機能あり。
    PR review hunk src/ui/pr_conversation/hunk.rs:27–40,94–126,128–180、caller pr_conversation.rs:309 Copy this hunk / このコードをコピー。raw ReviewComment.diff_hunk全量専用。投稿本文ではない。

    hunk buttonのIDは ("pr-convo-hunk-copy", number*1000+i)、clickは表示時の hunk.full.clone() をclosureに捕捉してCopyする。stable GitHub comment IDから本文を再検索するcommandではない。collapse表示12行でもfull hunkをCopyする点は保持する。ただしボタン自体はvirtual feedのrowがmountedのときだけ存在し、画面外任意投稿のglobal commandではない。

    Issueには PostKey::Comment(opaque ID) (issue_conversation.rs:26–28,252–259) があるが、それをtargetにする本文Copy actionは実装されていない。PRのflattened Entry (pr_conversation.rs:126–151) はcomment IDを持たず、index-based presentation。存在しないstable-ID buttonを移行済と記載しない。

    本計画の代替: 目的の投稿まで戻り、mounted TextViewの公式SelectAll/Copyを使う。raw GitHub Markdownではなく、既存PreparedBody→公式Plain-rendered selection→Copyのtrimという経路。外側unmount済の単一投稿をキーボードcommandだけで全文取得する保証は持たない。

    投稿全体のCopyボタン新設は、ownerが追加scopeとして望む場合のみ別途設計する(raw Markdown / prepared Markdown / rendered plainのどれをCopyするか、stable full owner identity、画面外targetの選択方法を決める必要あり)。今回は未依頼の新機能として追加しない。hunk Copyを本文Copyと誤表示して代用もしない。

    U1 — 最小の公式Textarea accessor(外部prerequisite、選択用U0とは別)

    追加調査で公開APIの代案まで確認した。

    • shrink/internal scroll側は既存public APIで足りる: TextareaState::new(...).rows(1) と Textarea::h(relative(1.))。外側をKagiがpreferred height / min_h(0) / flex_shrink(1)にすれば、現composerと同じ役割分担にできる。
    • 不足はpreferred3–12行を決める既存cached visual rowsへのread-only queryだけ。official InputPresentation (base/input/base/state.rs:564–625) にrows/height/scroll extentなし、631–650はcaret/viewportのみ。scroll_handle / scroll_size (488,496) はcrate-private。display_map()/prewrap() (11836–11865) はEditorMode限定。
    • Kagiにpublic DisplayMap をもう一個持つ代案自体は可能。しかし source/幅/font変更時に同じ本文を二重wrapping/shapingする。公式wrapperはUnicode break/grapheme・shaped prefix幅を使う (base/input/editor/display_map/text_wrapper.rs:32–111,365–435,525–614)。単なる文字数/GPUI LineWrapper代用は同じ行数を保証せず、12行clampしても第二mapの全logical-line処理量はboundedにならない。不要な計算/コピーと長期のwrap意味同期を持ち込むので採らない。
    • auto_growだけではleaf minがpreferred rowsになるため、現仕様どおりの縮小を阻害する。getterのためにcode EditorStateを使うとfont/indent/folding/search/keycontextまで増え、これも採らない。

    確定するupstream提案: TextareaStateに既存cacheの wrap_row_count() を返すO(1) getter(提案名 visual_row_count 等)を追加。frame前/幅変更後のcache更新タイミングをdoc化。別height-policy API/新rendererは不要。

    upstream変更3ファイル / [INFERENCE] 80–180 LOC:

    1. crates/base/src/input/base/state.rs:11660–11718(小accessor、colocated unit)
    2. crates/kit/tests/integration/input/textarea.rs:476–535,1120–1137,1208以降(plain rows1+Kagi式preferred/shrink layout、soft wrap/Unicode/resize/内部scroll)
    3. website/component/textarea.md:30–42,86–105(auto-grow minimumとapplication-owned preferred heightの違い)

    依存・merge条件: officialに正式mergeされたrevをS6で採用する。それまではS0〜S5を進められるが、全要件を保持した最終S6はmergeしない。API名/採用rev/acceptanceは未確定であり、調査中にPRは作成していない。拒否された場合、二重map・仕様縮小・forkを無断採用せずownerに差分を提示する。

    S6 — official package family + typed APIs + group撤去の原子的切替

    依存: S1/S2/S3/S4/S5-A〜D と U1のofficial取り込み。一つのgreen merge unit。新base/typed APIと依存identityが同時に変わるため、manifestだけ、Findだけ、Radioだけ、旧groupだけという赤い中間commitをdevへmergeしない。PR内の作業/レビューは以下のsubsectionごとに行えるが、mergeは一回。

    A. 7 manifest / 一つのGPUI identity / zero patch

    manifest:line 変更
    Cargo.toml:23,39–51,140–146,181–208 gpui/pre-platform、official component+base+kit-assets、feature forwarding、profile名、残patch全削除。
    crates/kagi-ui-core/Cargo.toml:17–18,36 gpui-pre/official component、Linux testのgpui-pre-wgpu。
    crates/kagi-ui-ecosystem/Cargo.toml:17–18 同一source/rev。
    crates/kagi-ui-editor/Cargo.toml:13,18–19 同一source/rev、test-support forwarding維持。
    crates/kagi-ui-file-history/Cargo.toml:19–20 同一source/rev。
    crates/kagi-web/Cargo.toml:20,23–24,31 gpui-pre / pre-web / pre-platformのtarget別指定。
    vendor/gpui-terminal/Cargo.toml:49–57 owned terminalも同じgpui-pre family。

    既にresolve実証した形(/tmp/kagi-fork-review/resolve/Cargo.toml:7–14):

    gpui = { package = "gpui-pre", version = "=0.3.9" }
    gpui_platform = { package = "gpui-pre-platform", version = "=0.3.9", features = ["font-kit", "x11", "wayland", "runtime_shaders"] }
    gpui-component = { git = "https://github.com/longbridge/gpui-kit", rev = "1188c11003b5cb5b83bc07d3fb9112e7674fbfa7", features = ["tree-sitter-languages"] }
    gpui-kit-assets = { git = "https://github.com/longbridge/gpui-kit", rev = "1188c11003b5cb5b83bc07d3fb9112e7674fbfa7" }

    これがそのままU1を含むという意味ではない。最終revはU1を取り込んだofficial実SHAへ全manifest一斉更新。base Radio/TextSelection::clearのpublic seamを使うrootには同じofficial revの gpui-base を直接追加する(component互換facadeに存在しない gpui_component::base というimportを発明しない)。既存 gpui_component::... の多くはofficial component facadeでそのまま残せる。Kit facadeへ全namespaceを機械置換する理由はない。

    • Cargo.lock 再生成。S1でcosmic削除済、S6でZed sibling 22 entries(184–205) とcomponent/assets 2 entries(207–208)、空patch headers/commentsを全削除。
    • [profile.dev.package] gpui は gpui-pre。gui-e2e以外の通常buildでtest-supportが無条件に入らない条件を維持。
    • src/ui/assets.rs:253,262: gpui_component_assets::Assets → gpui_kit_assets::Assets。
    • main/E2E/Settingsの Root::new(...).font(ui_font()) はpublic Styledのfontメソッドで成立するので、Root全面置換は不要(Kagi mod.rs:3516, e2e.rs:918, settings_window.rs:35)。

    B. typed Input / Textarea / Editorの全主要境界

    fresh grepでmulti_line builderは4か所(3種類のstate、Commit再生成が2回)、code_editor builderは3か所。single-line InputStateを全置換しない。

    Kagi file:line exact migration
    src/ui/commit_panel.rs:432,585–605 bodyだけEntity。titleはInputState。両方をInputState固定closureへ渡すreadを型別に分ける。
    src/ui/operations/commit.rs:108–114 / commit_panel_composer.rs:173–181 body作成/locale再作成をTextareaState::new().rows(1)。draft/template/set_value/replace_all ownershipは変更なし。
    commit_panel_composer.rs:56–61 / compact.rs:128 異なるentity型を同じ配列に入れる処理を分離。focus判定はFocusHandleへ射影して既存判定を再利用。
    commit_panel_composer_render.rs:28–35,87–91 U1 getterのcached行数clamp3–12をpreferredに使用。plain Textarea::h(relative(1.))、元のmin/shrink/clip/focus-ring/footerを維持。
    src/ui/pr_mode.rs:988–991 / mod.rs:1226 / pr_page.rs:577 PR本文composer→TextareaState/Textarea、auto_grow2–8はそのまま。
    src/ui/settings_view.rs:66–68,180,1133 / mod.rs:1219 analyze ignore→TextareaState/Textarea。setter/save/resetのwriter契約は変更なし。
    src/ui/issues_composer.rs:36,79–82,185,489 Issue body→EditorState::new().language(...)/Editor、titleはInputState。
    src/ui/conflict_view.rs:70,1020,1092–1095 / mod.rs:1411 / conflict_editor.rs:1567 Conflict result→EditorState/Editor。language/read-only/busy/approvalは既存値を保持。
    crates/kagi-ui-editor/src/lib.rs:276,495,1537–1540,1592,3300 open buffer/history editor state/construct/render→EditorState/Editor。historical selectable TextViewは別のまま。
    src/ui/timeline_row.rs:201–205 body_inputを型隠蔽enumにせず、PR Textarea / Issue Editorの薄いrender entryへ分ける。callerは上記2か所。
    src/ui/issues_composer_e2e.rs:185,289–290,395–398 返り値とtitle/body hetero iteratorをtypedに直す。fake統一InputStateを残さない。
    src/ui/window_menu.rs:67–68,131–132 / src/ui/render.rs:246–247 focused_inputがAnyInputStateになるので .read(cx).focus_handle(cx) ではなくpublic .focus_handle(cx)。modal_key_routing.rs:54–55 は既にこの形で原則不変。
    S4の core/input_actions.rs AnyInputStateの3variantsをpublic enumでmatch、同じIME capture/bubble契約を保つ。
    tests/recovery/editor_find.rs:6,22ほか, editor_find_hosts.rs:7,14, editor_snapshot_readonly.rs:23,92, menus_ja.rs:144, commit_panel_viewport.rs:188 testsのtyped境界とgetterを対応。single-lineのref-validation/remote input等は変更しない。

    composer replacement sketch(getterはU1の公式確定名に合わせる):

    // State: no auto-grow minimum. Textarea keeps editing/wrapping/scrolling ownership.
    let body = cx.new(|cx| TextareaState::new(window, cx).rows(1));
    // After upstream U1: query its existing cache, do not shape a second text copy.
    let rows = body.read(cx).visual_row_count().clamp(3, 12);
    let preferred = px(rows as f32 * theme::rem_size_px() * 1.25)
        + gpui_component::Size::Medium.input_py() * 2.;
    div().h(preferred).min_h(px(0.)).flex_shrink(1.).overflow_hidden()
        .child(Textarea::new(&body).h(relative(1.))
            .aria_label(Msg::A11yCommitDescription.t())
            .appearance(false).bordered(false))

    コード全体のcompile済例ではなく、既存composer frame内の置換sketch。3/11/soft-wrapped6行の現oracle(95/239/149px、ADR0134:200–223)と低window内部scrollを受入で照合する。

    C. Find / Radio / Select / Theme / Undo

    • Find: core/find.rs:1–17 を既存labels ownerのままKagiの薄いFind UIへ拡張。official Editor .searchable(false) でbuilt-in chromeだけ使わず、公開engine search_session()136、set_search_query()172、close_search()186、next_search_match()191、previous_search_match()200、replace_current_search_match()211、replace_all_search_matches()244(base/input/editor/search.rs)を使う。regex/match index/undo backendを二重実装しない。
      • 3 host: Editor、Conflict Result、Issue body。search_labels3builder+set_search_labels3更新を全撤去。
      • 2 named Inputs+7 named Buttons、既存EN/JA Msg、tooltip、replace-mode/case state、Tab/Shift-Tabの実順、Escape→buffer、padding click→Find focus、Button上のediting actionがhidden editorへ流れない境界を維持。
      • upstream cycle_focusはreplace mode時の2入力間だけで、button traversal/labels/help/containmentの代替ではない。
      • tests/recovery/editor_find.rs:22–28,73 のfork SearchPanelProbe/help-probeを削除。S4のnative AX /実focus /actual button bounds /hoverで呼ばれたTooltipを使う。official提供のtest queryを使う場合も実Element snapshotであり、名前定数の自己比較にしない。
    • Radio: settings_view.rs:699,713,750,965 の4groupを 新規 src/ui/settings_radio.rs の薄いKagi paintへ。official gpui_base::RadioGroup / Radio を使い、checked+disabled+AX+pointer/Enter/Spaceはprimitiveへ委譲。indicatorだけ現6/14px dotとprimary_foreground。既存4groupのcallbacksとstable element/focus IDsを維持。base RadioGroupはsemantic containerであり、存在しないautomatic roving-arrow機能を前提にしない。新しい設定storeや独自Radio event systemは作らない。
    • Select: settings_view.rs:503–520 のbuilderは .accessibility_label(...)。actual focused ComboBoxのrole/value/openはupstreamが所有。既存 ThemeOption::title のsanitize (99–108)を残し、AXだけ別raw名にしない。custom selection state/open flagを追加しない。
    • Theme: core/theme.rs:1271,1277 のdirect global_mut は新base projection/tokensと乖離する。official Theme::update(cx, |theme| ...) (component/theme/mod.rs:255–289) を使うか、同等に全projectionを同期。sync_baseだけではtokensはreconcileされないことにも注意。選択active色が同値の毎renderには更新しない(不要clone/refresh-loopを防ぐ)。色変更時のみreconcile+Base sync。全preset/selected/inactive/scrollbar/radio contrastを維持。
    • Undo: commit_panel_composer.rs:99,113、issues_composer_e2e.rs:235,255,290 等の replace_allは公式atomic groupingのまま。set_valueへの置換は禁止。smart_replace_undo は前後typingを含めsleep無しでUndo/Redo境界を保持。
    • Lifetime: fork b50aの旧strong next-frame経路はofficialにないが、pr_detail_lifecycle / smart_generation_close_drops_panel のnative release/orphan assertionsを実行して初めて移行完了とする。

    D. SDK group撤去とKagi既存ownership guardの整理

    設計を一つに絞る: SDK TextSelectionGroupRetirement / Root group / all-post members / outer edge taskは削除。ただし既存Kagi ConversationScope の active/generation はaccepted-row/deferred-render validityにも使われるので残す。これをofficial TextSelectionScopeIdに置き換えない。新しいcross-post model/parser/canonical byte cacheは作らない。

    • issue_conversation.rs:40–55 のSDK retirement field/呼出しを除去、:89–95 のSDK lease active check、:106,136–144,277–284 のgroup作成・publish、weak Root installation/defer部を除去。PostKey/source/retained TextView/geometry/acceptance世代は維持。不要になるsource_revisionは消す。
    • issues_thread.rs:3,135–158 のgroup viewport marker/held-edge callbackを削除。TextView 237–245 は公式の通常selectable/default scope。PR側の timeline_row::body_markdown / PR description /review/thread/previewも公式挙動を禁止するscopeを付けない。
    • source差替え: issue_conversation.rs:342–350,389–395、削除post判定 223–225 で旧選択をpublic TextViewState::clear_selection と必要なwindow clearで同期撤去してからtextを更新。clearをstate.updateの内側から同stateへ再入させない。refresh開始/失敗/無関係なpost更新で無条件全clearしない。
    • 古いpaint済routeからのCopy/SelectAll: src/ui/render.rs:690–703 の既存Kagi root action境界にcaptureを置き、paint時に捕捉した既存Kagi owner/generationがまだforeground/activeかを現在のapp状態で確認する。staleならpublic gpui_base::TextSelection::clear /必要に応じ clear_for_window で撤去し、そのactionをconsume。validなら一切加工せずofficial descendant/Rootへpropagateする。新しいselection scope/renderer/clipboard formatter/全post scanは作らない。
    • 捕捉するのは既存Scope scalarのRc/weak lifetimeであってIssueConversation/Input entityのstrong lifetimeではない。native inputが現在のvalid foregroundを持つ場合は通常どおりInputが先にCopy/SelectAllをconsume。modal/departed画面の古いInput routeを「現在のInput」と誤認して許可しない。
    • このcaptureを実証する必須ケース: post focus/Reply focus/Kagi root focusそれぞれから、same-update departure→Copyとmodal表示→Copy。Root fallbackがcaptureを迂回するならgate失敗であり、no-App Dropを扱うhidden leaseを再導入してごまかさない。owner-close/retarget/source mutationの実product eventから検証する。
    • 任意のno-App DropだけでSDK global内部stateが即空になるというfork固有保証は撤去。observable Copy/SelectAll actionと、公式actual-unmount frame sweepを受入対象にする。この区別をlifecycle testとADRで明記する。

    既存retire/activation callersはgeneration ownerとして意図的に維持(API shimではなく既存accepted lifecycle):
    github_issue_state.rs:84,231,244,292,322–342; github_issues.rs:158,175,190,249,277; issue_conversation.rs:457,482,496,532,579; issues_composer.rs:260,262; branch_cleanup.rs:49; command_palette.rs:332; ecosystem.rs:90; file_history.rs:241; overlay_focus.rs:366,410; pr_mode.rs:264; tabs.rs:217; workspace_mode.rs:635,745; operations/commit.rs:900; operations/conflict_detect.rs:288; operations/modal_state/arbitration.rs:48,66。

    この設計ならpure/no-cx modal setters全体にAppを注入する大改造は不要。depart_active_tab (tabs.rs:204, callers 267,399, home.rs:85,111)、leave_takeovers (workspace_mode.rs:621, callers 673,680,707,727,760)、leave_settings_for_overlay (overlay_focus.rs:365, callers about.rs:14, commands.rs:2662,2674) も既存generation失効の入口として残す。tab_view.rs:458–464,593–595 / tab_ui_state_ops.rs:148–150 のKagi lifetime holderと github_issue_state.rs:322–342 のコメントはaccepted-generation guardという用途に明確化し、SDK group storageとしては残さない。

    S3の13旧scenario改訂と5新規positive scenario、ADR0198/0136/0200/0142・decisions・CHANGELOG・verify skillを同mergeで切替。未変更のretirement callerを「SDK group caller取り残し」と誤判定しない一方、SDK型/import/methodのliteralがproduction/testに残らないことは確認する。

    S6 regressionの先行順・最終gate

    1. S3正条件を公式as-is oracleへ更新する前に、旧跨ぎ/画面外保証を削る箇所を上記13行と照合する。source/privacy/Input/repo不変のassertは削除しない。
    2. S4の実AX/Input/IME、S5の実geometryをbaselineとして使う。typed Input移行のcompile対応でtest oracleそのものを文字数近似や宣言定数へ変えない。
    3. Findは2inputs+7buttonsのnative role/name/help/focus/state/keyboard contaminationを先に書く。Radioはdot/foreground contrastとpointer/Enter/Space、Selectはactual ComboBox。Themeはbase projectionも含む実描画を比較。
    4. composerはU1のofficial integration testに加え、Kagiの既存 commit_panel_viewport の3/11/soft-wrapped6行、native height95/239/149px、低window・high zoomで内部wheel/入力/Commit footerを維持。font shapingをmockしない。
    5. selectionのold no-App SDK lease観測を、実product transition→Copy actionとofficial completed-frame unmountへ改訂。根拠なくnative失敗をskipせず、Root capture routingを修正する。

    S6の絞込みgate(全部実在するprefix、括弧の新規scenarioを除く):

    issue_conversation_,pr_conversation_post_select_all,pr_thread_post_select_all,markdown_preparation_edges,workspace_mode_toolbar,pr_same_number,pr_threads,review_hunk_full,issue_body_zoom,graph_copy_foreground,graph_pointer_focus,editor_,conflict_,settings_,sidebar_search_,input_names_inventory,input_ime_escape_boundaries,commit_panel_,smart_replace_undo,smart_generation_,pr_detail_lifecycle,pr_diff_extent_,diff_keyboard_selection,split_diff_keyboard_order,diff_selection_revision,terminal_,status_bar_,finder_scale_rest,inspector_scale,oplog_detail_select_copy

    新規は S3/S4節に列挙したscenarioだけ。S6はframework family全体が変わるため、絞込みだけで完了せず、通常の全native suite(filter無し、KEEP_GOING無し)と別実行のinventory smoke/比較、workspace testsを通す。inventoryとordinary filterは混ぜない(verify skill:2702–2706)。

    platform/dependency gate:

    • macOS default / gui-e2e build、workspace unit/integration、上記native GUI。leak assertionをoffにしない。
    • Linuxの実target build+S1のCosmic real-shapeとnative CJK matrix。macOS結果をLinuxの証拠にしない。
    • Windowsは対応runner上の実target compile、WASMは既存 scripts/build-web.sh と既存web harness。macOS上の無理なcross-link失敗を無視して全platform成功としない。
    • cargo metadata / cargo tree --target all --edges normal,build -i gpui-pre でactive GPUI identityが一つ、root/crate/CLI-configにpatch/replace/local-forkの隠し経路がないこと。optionalな非active gpui-fast metadata nodeの存在だけをduplicate active dependencyと誤認しない。
    • 全manifestsの [patch] ゼロ、lockにTomiXRM/zed・cosmic・gpui-kit fork sourceゼロ、official component/base/assets rev一致。pathのowned terminalは認めた例外として明示。
    • checksum付きログ、native screenshot/AX/clipboard/geometry、Linux font identity evidence、実行コマンド・対象SHAを残す。独立resolveの855 packages成功はKagi compile成功の代わりではない。

    S6 全変更予定パス(distinct 60)

    以下は上記設計で変更するパスを数えたもの。新しいAPIを導入するために無関係な全InputState参照を機械変更する一覧ではない。compile未実施なのでcompiler-drivenな小差分の予備は後述に別計上する。

    • src/ui/issue_conversation.rs
    • src/ui/issues_thread.rs
    • src/ui/render.rs
    • tests/recovery/issue_conversation_selection.rs
    • tests/recovery/issue_conversation_lifecycle.rs
    • tests/recovery/issue_enabled_input_copy.rs
    • tests/recovery/issue_offscreen_select_all.rs
    • tests/recovery/issue_conversation_support.rs
    • tests/gui_e2e_runner.rs
    • tests/recovery/conversation_official_selection.rs(新規)
    • docs/adr/0198-read-only-issues-workspace.md
    • docs/adr/0136-pr-merge-and-review-conversation.md
    • docs/adr/0200-pr-workspace-layout.md
    • docs/adr/0142-markdown-image-rendering.md
    • docs/decisions.md
    • CHANGELOG.md
    • .claude/skills/verify/SKILL.md
    • src/ui/github_issue_state.rs
    • src/ui/tab_view.rs
    • src/ui/tab_ui_state_ops.rs
    • Cargo.toml
    • Cargo.lock
    • crates/kagi-ui-core/Cargo.toml
    • crates/kagi-ui-ecosystem/Cargo.toml
    • crates/kagi-ui-editor/Cargo.toml
    • crates/kagi-ui-file-history/Cargo.toml
    • crates/kagi-web/Cargo.toml
    • vendor/gpui-terminal/Cargo.toml
    • src/ui/assets.rs
    • crates/kagi-ui-core/src/theme.rs
    • crates/kagi-ui-core/src/find.rs
    • crates/kagi-ui-core/src/input_actions.rs
    • src/ui/commit_panel.rs
    • src/ui/commit_panel_composer.rs
    • src/ui/commit_panel_composer_render.rs
    • src/ui/compact.rs
    • src/ui/mod.rs
    • src/ui/settings_view.rs
    • src/ui/conflict_view.rs
    • src/ui/conflict_editor.rs
    • crates/kagi-ui-editor/src/lib.rs
    • src/ui/window_menu.rs
    • src/ui/settings_radio.rs(新規)
    • tests/recovery/editor_find.rs
    • tests/recovery/editor_find_hosts.rs
    • tests/recovery/editor_snapshot_readonly.rs
    • tests/recovery/menus_ja.rs
    • tests/recovery/commit_panel_viewport.rs
    • docs/adr/0001-gpui.md
    • docs/adr/0006-gpui-component.md
    • docs/adr/0080-settings-window.md
    • docs/adr/0134-simpler-commit-pane.md
    • docs/adr/0125-apple-hig-themes.md
    • docs/adr/0222-diff-list-estimated-height.md
    • src/ui/timeline_row.rs
    • src/ui/pr_mode.rs
    • src/ui/pr_page.rs
    • src/ui/issues_composer.rs
    • src/ui/issues_composer_e2e.rs
    • src/ui/operations/commit.rs

    S6 churn見積り: [INFERENCE] 4,200–6,900 LOC(typed API/Find/Radio/theme/assets/selection/tests/docs、generated lock除外)。そのうちgroup撤去/既存guard整理/oracle改訂は約1,200–2,000 LOC。旧案の38-file App-aware setter全面移行は採らず、既存Kagi validity tokenを実action境界で検証するため、no-cx modal setter chainの100か所規模の引数変更をscopeへ持ち込まない。別途、未コンパイルの公式API差分に対する予備 0–20 files / 400–900 LOC を想定する。この予備を確定変更・検証済として数えない。

    最大リスク: U1のupstream accept待ち、typed APIを一つでも残すこと、Rootのaction propagation/Fallbackとretired ownerの関係、official Markdown/Copyの分離記号/trimを旧fork canonical streamと同一視すること、Theme base/tokensの同期漏れ。U0や独自rich rendererの再導入は解決策にしない。

    仕上がりの順序・merge単位・依存関係

    unit merge対象 depends_on files_count churn LOC(lock除外)
    S0 Kagi方針/出自 — 6 90–160
    S2 Kagi-owned terminal ADR/notice S0 6 110–190
    S1 native fallback + cosmic patch除去 S0,S2 24 550–950
    S3 mounted単一投稿の正条件準備 S0 4 250–400
    S4 Clear/IME/native AX依存準備 S0 15 450–750
    S5-A PR lane uniform / RowScroll API S0 8 250–450
    S5-B Inspector/restore uniform S5-A 10 450–800
    S5-C shared variable list / 全diff S0 22 2,250–3,700
    S5-D Issue feed/conversation geometry S5-A,S5-C 12 800–1,400
    U1 official Textarea cached accessor —(upstream) 3 upstream 80–180
    S6 official family+全typed API+selection最終 S1,S2,S3,S4,S5-A〜D,U1 60 4,200–6,900

    推奨Kagi merge順は S0 → S2 → S1 → S3 → S4 → S5-A → S5-B → S5-C → S5-D → S6。U1はS0後すぐ別upstream作業として進められるが、取り込みまでKagiは現pinのgreenを保つ。S5-CはA/Bとは独立して先行可能。shared file編集はmerge/rebaseで一人が統合し、別々に半分のAPIをmergeしない。

    S6以外の各unitは現依存pinで独立greenにできる範囲だけを採る。S3の「準備」とS6の「保証撤去+official化」を区別してchangelogに記す。S5-Dはまだ旧group consumerを維持し、S6で最終撤去する。移行完了後に旧path/flag/aliasを残す計画ではない。

    未確認事項・完了を妨げる条件

    1. U1のofficial採用SHA/API名は未確定。公開APIを調べた上で残った最小prerequisite。Kagiをforkで先に動かして既成事実にしない。
    2. Kagi全体は本調査でcompile/runしていない。S6の追加compile調整、native selection/IME/Find/Radio/Themeの統合結果は未検証。依存resolve成功だけをgreenの証明にしない。
    3. variable-list新実装の実Kagi latencyは未測定。過去measure_all数値と将来gateを明確に分けた。production measure_all採用は0であり、測っていない256rowを性能根拠にしない。
    4. Linux CJKはこれからS1/S6で実施するgate。前調査はコードの経路確認と既存テストの特定であり、この追補でLinux画像を取得したとは主張しない。
    5. 投稿全体Copyボタンは存在しない。画面外任意投稿を既存stable-ID Copyで代替できるという前提は否定済。今回の仕様はmounted公式操作であり、新ボタンは別のowner scope決定が必要。
    6. official mounted cross-viewは禁じない。その結果が将来upstreamで変わってもKagi独自の全会話契約として固定しない。変更しないprivacy/accepted-source/Input/draft/repository invariantsとの境界は上記oracle表を基準とする。
    7. no-App DropによるSDK内部state即時失効は旧fork固有保証として撤回。実際のforeground transitionからのstale Copy/SelectAllをKagi public action境界で防ぐ設計は、既存same-update native regressionsで必ず実証してからmergeする。

    調査の変更範囲: Kagi本体/worktreeへの変更ゼロ、commit/push/PR/issue commentなし。ここで新規作成したdeliverableはこの local://fork-review-1479-plan.md だけ。実行済の数値は前報のthrowaway list measurementを出典付きで再利用し、今回のplanning中にbuild/test/formatterは実行していない。

  5. added 10 commits that reference this issue on Oct 15, 2026
  6. 1 remaining item

  7. TomiXRM commented on Oct 16, 2026

    @TomiXRM
    OwnerAuthor

    U1: TextareaState::visual_row_count() の最小公開APIを実装しました。既存cacheのO(1)参照のみで、追加のshaping/copy/cacheはありません。公式upstreamへの提出用commit: TomiXRM/gpui-kit@d24685a(Kagiの依存先には採用しません)。base unit 1,375件、Kit integration/UI 395件、Clippy、fmtを通過。macOSのnative text system + Metalによる使い捨てsmokeでも3/6/20行、120pxへの縮小、内部scroll、Unicodeの幅変更を確認しました。添付はそのnative描画です。公式repoはREAD権限で画像添付が拒否されたため、ここに検証画像を保存します。S6は公式merge後のrevisionを採用する方針を維持します。

    Native Metal textarea

  8. TomiXRM commented on Oct 16, 2026

    @TomiXRM
    OwnerAuthor

    #1479-S6 進捗 — official U1 merge 待ち

    不足していた cached visual-row API は公式 longbridge/gpui-kit#3540 に提出しました。PR は OPEN / MERGEABLE、macOS・Linux・Windows を含む upstream CI は通過しています。提出用 fork branch を Kagi の依存には使っていません。

    Kagi は隔離 worktree deps/s6-official-cutover で作業中です。まだ push / PR / merge していません。

    • manifest の [patch] を撤去。全 target の normal/build dependency closure は active gpui-pre =0.3.9 が1系統、TomiXRM fork source は0、component/base/assets は同一 official revision。
    • Input / Textarea / Editor、公開 search engine 上の Find、base Radio、Theme tokens/base 同期、公式 TextView/default scope への切替を実装。
    • default cargo build は TextareaState::visual_row_count() が未公開の1エラーまで到達。公式PRが merge された revision に一括更新するまで、Kagi全体を green と扱いません。
    • kagi-ui-core library: 309 passed / 9 ignored。kagi-ui-editor library: 42 passed。
    • cargo fmt --all --check、両 UI crate の Clippy、uv run --project ci check-all 通過。Clippy は既存箇所の警告あり。
    • Settings Select は fork の is_open / renderer-fed option recorder を廃止し、実際の native AX tree を読むテストへ移行。全体の native runner は未実行です。

    公式依存のみの core 描画スモーク

    変更した kagi-ui-core を一時プログラムから実行し、macOS の実 text system + Metal で EN/JA × Apple Light/Dark の4条件を描画。Cmd-F → alpha の3件一致 → Replace all → Escape → 1回の Undo で元の日本語を含む本文に戻ることを確認しました。一時ソースは削除済みです。

    これは offscreen core smoke であり、Kagi全体の Tier A/B、OS AX・VoiceOver・IME の証明ではありません。

    EN / Apple Light — core Metal smoke

    JA / Apple Dark — core Metal smoke

    次は official #3540 merge → component/base/assets の同時 revision 更新 → Kagi全体の build・workspace tests・native/各platform gate → greenなS6 PRです。fork fallback や local SDK patch は追加しません。

  9. TomiXRM commented on Oct 16, 2026

    @TomiXRM
    OwnerAuthor

    Textarea 上流APIの修正版とネイティブ描画証跡

    上流の #3540 はメンテナー判断で閉じられました。専用の visual_row_count() は追加せず、指定された既存の display_map() / prewrap() を全入力モードに共通化する修正版を準備しました。

    検証したコミット: 6a8f0ebb81a12d4095371e8844cfeb112458f109

    • 基礎 unit: 1,384 passed(test-support 有効)。Kit integration/UI: 400 passed。
    • cargo fmt --all -- --check、cargo build -p gpui-base -p gpui-kit、対象 crate の Clippy が成功。依存 block v0.1.6 の既存 future-incompatibility notice あり。
    • macOS の実テキストシステムと Metal renderer を使ったオフスクリーン描画で、初回 prewrap、内容に応じた preferred height、120px の制約下での縮小・内部スクロール・キャレット可視性、日本語/結合文字/家族絵文字の折り返し、初回レイアウト後の prewrap no-op を確認。下記画像は今回の修正版から取得しました。
    • 通常の基礎 unit コマンド(test-support なし)は、この checkout の a11y_disabled.rs:298 が feature-gated な crate::test_support を参照して停止したため、必要な feature を有効にして実行しています。無関係なテストの修正は混ぜていません。

    Native Metal: wide textarea

    Native Metal: constrained textarea with Unicode wrapping

    これは Kagi 全体の依存切り替え完了や、OS の IME/Accessibility 検証を意味しません。公式上流への取り込みを待ち、その revision で Kagi の依存と呼び出し側を切り替えます。投稿用 fork を Kagi の依存先にはしません。

  10. TomiXRM commented on Oct 16, 2026

    @TomiXRM
    OwnerAuthor

    修正版を公式 upstream の longbridge/gpui-kit#3564 として提出しました。longbridge/gpui-kit#3564

    Textarea の前提となる追跡先は、閉じられた #3540 ではなく #3564 の公式取り込みです。検証済み head は 6a8f0ebb81a12d4095371e8844cfeb112458f109。新設 getter ではなく共通の display_map().wrap_row_count() / prewrap() を使用する方針で、直前のコメントに native Metal の画像とローカル検証結果を添付しました。上流 CI は実行中です。

    S6 は引き続き未完了です。投稿用 fork を依存先にする回避は行わず、公式 merge 後に採用 revision・Kagi の呼び出し側・全 platform/IME 等の受入 gate をまとめて確認します。

  11. TomiXRM commented on Oct 16, 2026

    @TomiXRM
    OwnerAuthor

    公式 upstream #3564 の head 6a8f0ebb81a12d4095371e8844cfeb112458f109 は、CI と Docs build の 16 checks がすべて成功しました。macOS/Linux/Windows tests、GPUI Fast の各 platform/WASM、Shell Core/Components/Standard Runtime、Lint、no-default-features を含みます。

    PR は OPEN、レビューコメントは現時点でなく、残る外部待ちはメンテナーのレビュー/公式 merge です。これは Kagi 本体の S6 や Windows/Linux GUI・日本語 IME の受入完了ではありません。公式取り込み前に投稿用 fork へ切り替えることはしません。

  12. TomiXRM commented on Oct 18, 2026

    @TomiXRM
    OwnerAuthor

    S6: Find の置換対象退行を公式 SDK 側で修正

    公式 SDK 移行中に、後方の一致箇所を表示してから新しい検索語を入力すると、即時の Replace が先頭の画面外の一致箇所を変更する問題を再現しました。大小文字の切替でも同様です。

    公式 PR: longbridge/gpui-kit#3584

    • 修正前: 表示中の row 90 ではなく row 20 が変更される2ケースが失敗。保持した検索の再開ケースは成功。
    • 修正後: Base 1,407件、Kit integration/UI 404件が成功。実プラットフォームの文字描画+Metalでも3ケースの置換対象・Undo・組込み検索パネル非表示を確認。
    • Kagi 側の Editor / Conflict Result のズーム退行も再現し、100%→167%で実際の文字幅・行間・カーソル高が追従する native 回帰シナリオ2件は修正後成功。

    S6 全体はまだ未完了です。 公式 PR のマージ・採用、Find の実キーボードフォーカスと AX フォーカスの紐付けの調査、残りの native / platform ゲートを継続しています。Kagi の依存を contribution fork に向けることはしません。

    以下は公式修正後の置換操作の前後です。修正前バイナリの比較画像ではありません。置換後は比較用に表示位置だけを元へ戻しています。OS ウィンドウ/AX/IME の検証証拠とは区別しています。

    置換前

    置換後:表示中だった row 90 のみ変更

  13. TomiXRM commented on Oct 18, 2026

    @TomiXRM
    OwnerAuthor

    S6 検証の進捗(まだ切り替え完了ではありません)

    公式依存だけを使う候補 gpui-kit@2062380587752a0095b4f49ca870cfde54d701c6 / gpui-pre =0.3.9 で追加検証しました。fork pin・path patch は追加していません。

    通過したもの

    • macOS cargo test --workspace: 3,543 passed / 48 ignored。
    • commit_panel_viewport と smart_replace_undo: 実 native runner で 2/2 PASS。composer の3行・11行・折り返し6行は引き続き 95 / 239 / 149px。低いウィンドウ・高倍率での内部スクロール、編集、footer の到達性を維持しています。
    • viewport テストは SDK の既定ボタン高や既定pane幅の固定値を削除し、新しい値への付け替えはしていません。製品側の行数・縮小・表示範囲の契約は保持しています。
    • タイトルだけのルール提案では本文は変更されず、本文 Undo に空の履歴を追加しないことを確認。本文を変更する生成経路は atomic Undo/Redo と以前の手入力・保存draftを引き続き検証しています。
    • Linux aarch64 の実 Kagi view fixture: X11 / native Wayland × ja_JP / en_US × 最小 / system fontconfig = 8条件。全画像を確認しました。実 PTY の通常/Bold、日本語2セルとcursor位置、Inter/JetBrains MonoのASCII、明示Noto Sans JPと一致する日本語FontIdを検証しています。Commit title/body、TextView、Diff、一般ラベルにも欠落glyphはありません。
    • Linux の実 CosmicTextSystem: 上記2locale × 2font環境の 4実行、計10テストPASS。Nerd Fontのicon優先順位とRegular/Boldの日本語fallbackも検証。macOS専用runnerのLinux空実行を証拠にはしていません。
    • Linux画像は検査marker直後の未描画Terminalを一度検出して棄却し、元のfixture手順どおりpresentationが落ち着いてから再撮影しています。最終8画像はTerminalの実描画を含みます。使用したapp・Xvfb・compositorは全て停止済みです。

    公式側へ提出した修正

    • gpui-kit #3585: Input/Textarea/Editorの実keyboard focusとnative accessibilityの意味付けを同じ編集要素に揃える修正。実AppKitの失敗前/成功後と、upstreamのLinux/macOS/Windowsを含むCIを確認済み。
    • gpui-kit #3586: Popover内のEnter/Spaceが親の開閉に奪われる不具合。公開componentだけの失敗前/成功後、実AppKit/Metal上の子ボタン・Textarea・Escape・focus復帰を確認済み。ローカル Base/Component 2,102、Kit 402、Metal 13 が通過。現在upstreamはTest Docsのみ通過しており、Rust CIと公式mergeは未完了です。
    • 既報の #3583(caret)と #3584(Find)も含め、現時点では4件ともOPENです。投稿したcontribution branchをKagiが参照することはありません。

    残っている受入

    公式merge後の同一revisionへの一括採用、native AX/Find/compact Popoverの再検証、安全な明示filterによる全native suite、残りのplatform/web/inventory受入が必要です。S6を完了・リリース可能とは扱っていません。

    添付はLinuxの8条件と、実macOSウィンドウでのSource Editor / Conflict Resultの100%→170%比較です。見た目も確認をお願いします。

    x11 ja_JP minimal

    x11 ja_JP system

    x11 en_US minimal

    x11 en_US system

    wayland ja_JP minimal

    wayland ja_JP system

    wayland en_US minimal

    wayland en_US system

    Source Editor 100%

    Source Editor 170%

    Conflict Result 100%

    Conflict Result 170%

  14. TomiXRM commented on Oct 18, 2026

    @TomiXRM
    OwnerAuthor

    S6追加検証:Linuxの独立オーバーレイとCopyの所有者境界

    S6は未完了です。未マージの上流修正をKagiの依存先に採用していません。 以下は追加で実行・目視確認した範囲です。

    Linux:独立した描画ルートでもフォントが保たれること

    X11 / Wayland × JA / EN × 最小フォント環境 / システムフォント環境で、実際の描画とネイティブのフォント選択を確認しました。

    対象 通過条件数
    Detached Tooltip 8
    Drag preview 8
    実際のWorktreeHoverCard 8

    24枚を個別に確認しました。以下は対象部分を元ピクセルから切り出した一覧です。Dragは毎回、独立した検証用display内で実際にpointerを移動させ、前のケースの位置に残ったghostを誤って撮らないようにしています。ユーザーのデスクトップやポインターは操作していません。

    Detached Tooltip / Drag preview

    WorktreeHoverCard

    負の対照も確認済みです。 各ルートの明示的なUIフォント指定をLinuxの検証用コピーで一時的に外すと、Tooltip / Drag / Worktreeの3ケースすべてが期待する日本語フォントのidentity検査で失敗しました。元のソースへ戻して再ビルドすると3ケースとも成功し、復元後の3ファイルは元のSHA-256と一致しました。復元時のmtimeにより一度古いCargo成果物が再利用されたため、その結果は採用せず、復元済みソースを再コンパイルした結果を使用しています。

    Linux上のTooltipの既存4回帰テストも通過しました。検証用アプリ・display servicesは停止し、一時exampleは保存済み証拠を残して削除しました。このLinuxコピーは先のLinux本体8条件と同じ移行スナップショットです。後述の最新macOS Copy修正までLinuxで検証した、という主張ではありません。

    macOS:現在の画面だけがCopyを受け取ること

    • terminal / status / typographyの19シナリオが通過。デスクトップ用fixtureが高ズームでcompactに切り替わっていたケースは、意図するlayoutのまま計測するよう修正しました。文字の収まりの条件は弱めていません。
    • PR diffのextent、graph/diff選択、Operation Log Copyの14シナリオが通過。
    • modal表示・画面離脱・別ownerへの切替を含む会話Copyの6シナリオが通過。拒否したcaptureではイベント伝播も止め、非表示になった古いReply InputにCopyが流れないようにしました。
    • 通常サイズのPreviewも、拡大composerと同じく実際のTextViewの選択をCopyできるようにしました。workspace toolbar / Markdown preparationの両シナリオが通過。

    残る公式SDK側の前提条件

    新たに longbridge/gpui-kit#3588 を提出しました。保持していたTextViewをunmount→remountすると古い選択が復活する問題を、既存の選択終了処理で修正します。独立レビュー、失敗→成功の回帰、実AppKit/Metalの部分選択とSelect All、Base 1,413 / Component 702 / Kit 400 / UI 1テスト、実Metal 13チェック、fmt / Clippyを通過し、push後のremote SHAも一致しています。Kagiへの採用は公式merge後です。

    もう一つの「ドラッグ直後、次のpaintより前にCopyすると古い範囲がコピーされる」問題は修正中です。最初の案はレビューでカスタム要素・スクロール座標・仮想化した端点への回帰が見つかったため採用していません。180段落の同一update内Copy要件は削除していません。

    既存の上流前提条件は gpui-kit#3583 / #3584 / #3585 / #3586。#3586はTest Docsのみ通過が確認できており、Rust CI全通過とは扱っていません。公式merge後の依存同期、全native suite、現在の実画面、Windows実target、WASM / web harnessなどが残っています。

    スクリーンショットの日本語の読みやすさと、独立オーバーレイで本文とフォントが揃っているかも見ていただけると助かります。エージェントの目視確認とは別に、人間の確認結果はまだ受領していません。

  15. TomiXRM commented on Oct 18, 2026

    @TomiXRM
    OwnerAuthor

    S6追加進捗:同一update内Copyの上流修正とWASM実行

    gpui-kit#3589 を提出しました。ドラッグ直後、次のpaintより前のCopyが古い範囲を返す問題への修正です。

    最初の修正案は採用せず、既存のcompleted-frame情報とAST所有の選択範囲を利用する形に変更しました。カスタムinline/block、仮想化で消えた部分選択の端点、スクロール直後の座標、source置換を維持し、Copyのために再描画・focus変更・別の選択registryを追加していません。レビューで見つかったnative line配列の二重収集も除去しました。

    • 10個のCopy回帰が通過。
    • 実際のAppKit/Metalで、180段落のPlain / Sourceの正確なCopyを確認。 head/tailの実描画で仮想化を確認し、最後のMouseMoveとCopyの間にはpaintを挟んでいません。縮小選択とsource置換も確認しました。
    • Base 1,421 / Component 702 / Kit integration 400 / UI 1テスト、実Metal 13チェック、fmt / Clippyが通過。既存の警告のみです。
    • push後のremote headは c27abc336552128890e76b5ba4c492f9e119abec と一致。再現用native consumerの全ソースはPR本文にあります。

    Kagi側の現在の通常gate

    最新の会話Copy capture修正を含むmacOS default build、workspace 3,543テスト成功・48 ignored、fmt、workspace Clippy、uv run --project ci check-all が通過しました。これは全native GUIシナリオの完了を意味しません。Settingsを含む残りのnative検証は、明示filterを付けた小分けのプロセスで継続中です。

    WASM / 実ブラウザ

    既存の scripts/build-web.sh が nightly 1.101.0 / wasm-bindgen 0.2.123 で成功しました。実ChromiumでBrowserWebGpuを選択し、既存catalogを起動。1280→900 logical px、20px刻みの全20条件で、最初のresize frameと200ms後のPNGが一致し、page errorはありませんでした。

    Retinaの実DPR 2とcanvasの物理サイズが一致した状態で検証しています。初期のCDP DPR-1試行はdevice-pixel-content-boxとの不整合があったため採用していません。以下の両画像は目視済みです。既存Playwright specの起動・resize操作をbrowserから直接実行した結果で、Playwright suite自体の実行結果ではありません。また、この既存web harnessはpure-domainの描画catalogであり、Git backendやKagi全体をWASMで動かしたという意味ではありません。

    Web catalog: 1280 logical px, DPR 2

    Web catalog: 900 logical px, DPR 2

    検証用browserとHTTP serverは停止済みです。上流PRのbranchやフォークへの依存差し替えはしていません。前の追加検証の #3588 と今回の #3589 を含め、公式merge後の依存同期・全native受け入れ・Windows実target compileなどが残っており、S6はまだ完了扱いにしていません。

  16. TomiXRM commented on Oct 18, 2026

    @TomiXRM
    OwnerAuthor

    S6 追加検証:通常 Preview Copy・Settings スクロールバー

    S6 の移行完了報告ではありません。Kagi の依存は引き続き公式 2062380587752a0095b4f49ca870cfde54d701c6。下流 fork・patch・contribution branch の pin は追加していません。

    通常 Reply Preview の実操作

    隔離した通常ビルドの実ウィンドウで、未送信の **S6_NATIVE_PREVIEW_20261018** を Preview に切り替え、本文クリック → Cmd+A → Cmd+C。クリップボードは Markdown 記号なしの S6_NATIVE_PREVIEW_20261018 と完全一致しました。元のクリップボードは全 representation を復元し、未送信 draft を消去して正常終了。GitHub への投稿は行っていません。日本語 IME の証明ではありません。

    通常 Preview の選択

    常時表示バーの予約レーン修正

    非 auto-hide 時の 16 logical px の予約レーンを、新 SDK の既定 handle viewport が含まないため、thumb が 16 px 内側に描かれていました。既存 wrapper の persistent 分岐だけに公式 viewport_from_layout() を指定しています。

    同じ 715×1064 logical px の実 Settings ウィンドウで比較しました。修正前は予約レーン x=707 のドラッグが効かず、内側 x=691 の thumb なら移動。修正後は x=707 で末尾の「Lock worktree while terminal is open」まで到達します。テスト座標・期待値は緩和していません。

    Before:予約レーンより内側の thumb
    After:予約レーン内の thumb
    After:日本語 167% で末尾まで到達

    実画面で EN/JA × 100%/167% の4条件を確認済み。auto-hide は wheel による表示・移動、thumb ドラッグ、バー外の非操作領域をドラッグした後の消失を確認しました。単純な PID-targeted MouseMoved だけでは thumb が残ったため、その入力経路を fade 成功の証拠にはしていません。SDK の不具合とも断定していません。

    Auto-hide:バー外への移動後に消失

    スクロールバーの OS 設定は固有 bundle ID dev.kagi.agent.scrollbar.20261018 の domain にだけ設定し、ユーザーの global 設定は変更していません。検証プロセスはすべて exit 0、専用 preference domain も削除しました。

    Gate と残件

    • 修正前に失敗した既存 Settings drag matrix は修正後 PASS。近傍9シナリオも PASS。
    • Settings/menu/folder/consent の scoped native シナリオは計37件 PASS。
    • cargo fmt --all --check、cargo build --locked、cargo test --workspace --locked、cargo clippy --workspace --locked、uv run --project ci check-all PASS。workspace は 3,543 passed / 48 ignored。Clippy の既存 warning は残っています。
    • Rust 1.96 の transition/presence duration 丸めを直す 公式 PR #3590 を提出しました。独立した Linux/macOS full package suites と実 AppKit/Metal の 99ms retained / 100ms absent を検証。既存 assertion は変更していません。#3589 の Linux 検証補足 も公開済み。

    公式 prerequisite の merge/adoption、残りの safely batched native acceptance、同期済みの各 platform gate、Windows 実ターゲット compile は未完です。現在の成果を S6 全体の受入完了とはしません。

    スクリーンショットの配置・読みやすさについて、人の目でも確認をお願いします。

  17. TomiXRM commented on Oct 18, 2026

    @TomiXRM
    OwnerAuthor

    S6 — Git 2.39修正統合後のmacOS / Linux全workspace検証

    独立した互換性修正 #1658 は 9245159371cfc3c0a281c3547a1db217b4d2e730 でmainへmerge済み、#1657はclose済みです。その4ファイルの修正を、未commitのS6移行作業版へ取り込みました。SDK pinは公式 2062380587752a0095b4f49ca870cfde54d701c6 のままです。新しいfork pin / patch / shimは追加していません。

    統合作業版 実行結果
    macOS arm64 format、default build、全workspace 3,543 passed / 48 ignored(162 suites)、Clippy、check-all 成功。既存のClippy警告あり。
    Linux aarch64 / Git 2.39.5 default build、全workspace 3,533 passed / 50 ignored(166 suites) 成功。生成されたdefault binaryの --version は kagi 0.46.0。

    Linuxは同一の統合ソースに対する cargo test --workspace --locked の結果です。以前の「S6非backend 2,188件」と「独立branch backend 1,345件」を足して合格扱いしたものではありません。1 build job、tini -s、非root verifierで実行し、rootのpermission bypassやPID 1のzombie retentionを製品の挙動と混同していません。

    追加のnative検証では、Branch picker / Activity / Inspector / bottom panelの34シナリオが成功しています。Activityは言語・theme・zoomとnative resizeをsettleさせてから180pxのpaneを開き、hover/keyboardのthree-pane検証はdesktop幅を整数logical pixelで指定するようfixtureを修正しました。既存のAX・localization・paint assertionsは維持しています。先のSettings等37シナリオと、今回のplatform workspace検証は別の実行記録です。

    通常ビルドのJA Activity画面も確認済みですが、これはVoiceOver検証ではありません。先のPreview Copy / scrollbar実画面記録も、workspace testsで代替したとは扱っていません。canonical verification skillとmigration documentに再現条件・適用範囲を追記済みです(skill 更新済み)。

    まだ完了ではない範囲

    公式GPUI Kit #3583–#3586 / #3588–#3590は、今回のAPI照会でも全て OPEN です。contribution branchはKagiから参照していません。公式merge後の依存更新、残りのnative採用ゲート、Windows実target等の最終同期検証が必要です。S6自体をcommit / push / merge / releaseしたとは主張しません。

  18. TomiXRM commented on Oct 19, 2026

    @TomiXRM
    OwnerAuthor

    2026-10-19 — 公式 prerequisite 2件が merge 済み

    上流の実際のPR状態とcheck結果を再確認しました。

    • GPUI Kit #3586(Popover内のEnter/Space所有権): MERGED。merge commit af456ddb2fe7bc57f040bb94064135cf353488d6。
    • GPUI Kit #3590(transition/presence duration精度): MERGED。merge commit 57aff71ef446e9cbd702a0e462c5813e2fd9a7b7。

    maintainer更新後の各headについて、Rustを含む19 checksがすべてSUCCESSです。macOS / Linux / WindowsのTest、GPUI Fast、WebView、Shell Runtime等を含みます。以前の「Test Docsのみ/Rust CI未確認」という保留状態は、この2件について解消しました。これは上流SDKのCIであり、KagiのWindows実行検証を済ませたという意味ではありません。

    残る公式 prerequisite は #3583・#3584・#3585・#3588・#3589 の5件で、現在もOPENです。S6の最終公式依存採用・同期済みnative/platform受入は引き続き未完です。contribution branchや追加forkへのpin差し替えは行いません。今回のMarkdown/PDF/branch-copy PR #1664も依存cutoverとは分離しており、既存GPUI pinを変更していません。

  19. TomiXRM commented on Oct 19, 2026

    @TomiXRM
    OwnerAuthor

    前提 PR の状態を公式 API で再確認しました。#3583(caret reveal、11:25Z)、#3584(Find anchoring、11:27Z)、#3588(selection unmount、11:40Z)、#3589(同期 rich Copy、11:44Z)が 2026-10-19 に MERGED です。既に取り込まれた #3586 / #3590 と合わせ、残る未マージの前提は longbridge/gpui-kit#3585(native input accessibility focus ownership、head cbde718bf9cc95e75ebe8e30506daf04ecaa820c)です。#3585 に新しい review / issue comment はありません。今回確認した4件には更新後の CI がまだ QUEUED / IN_PROGRESS のものがあり、全チェック完了とは扱っていません。

    公式 main は d476c7635ce03e4100cec67b6cac88014cb79de0。S6 は引き続き公式への一括 cutover と Kagi 側の native / platform / dependency acceptance が必要です。新しい fork pin や contribution branch pin は追加していません。

    関連する #1275 は現行 pin と公式公開 API の差分を再調査しました。公式には TextViewState::list_state()/bounds() があるため、S6 後は同一 selection/scroll owner のまま gutter geometry を検討する経路があります。no-wrap/horizontal scrolling と Copy/性能の受入条件は未完了です。通常 GUI の JA Dark 167% の確認結果: #1275 (comment)

  20. TomiXRM commented on Oct 19, 2026

    @TomiXRM
    OwnerAuthor

    公式移行の残件 #3585 に対する maintainer review を修正し、同じ PR に追加コミットを push しました。

    longbridge/gpui-kit#3585 (comment)

    • common::input/input_in を削除。fixture の accessible name と明示的な scoped/named query に移行。
    • 最新公式 main(v0.8.0)を取り込み、head 945ff36718e1f22f56142185dfc7efa3834d3fe7 と contribution remote の一致を確認。
    • 実 AppKit/AccessKit の editor focus・双方向 Tab・addon 独立動作が成功。
    • 統合後 Base 1433 / Component 706 / Kit integration 414 / UI 1 / 実 offscreen 描画 13 件成功。fmt/Clippy 完了、新規警告なし。

    再レビューはコメントで依頼済みです(GitHub の正式な review 再依頼 API は権限不足)。現在は更新 head の CI と maintainer 再レビュー待ちです。Kagi 本体の依存 pin はまだ変更していません。既存の S6 前提は引き続き #3585 の公式 merge と採用後の受入検証です。

    別途、PDF 境界操作で発見した disabled Button の Tab 停止は、承認済みの公式 PR #3606 / Kagi #1677 で追跡しています。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions