feat(account): one participant list, and a dialog to make an account in - #61
Conversation
…count protocol を 4 から 5 へ上げ、`hello` と `room_join` に任意の `account_id` を足した。部屋は席に載せて名簿で返すだけであり、判定には 一切使わない。自分の発言の抑止・名簿の同一性・`speaker` の刻印はいずれ も接続で決まったままである(#39 / #40 / #47)。運ぶ理由は画面側にある ——アカウント一覧と部屋の名簿を、名前ではなく id で突き合わせられる。 名前で結ぶ形は #40 と #53 が却下しており、リストが二つに割れていた直接 の原因がこれである(#57)。 アカウント id は任意である。持たない接続も参加者であり、部屋はアカウン トの存在を前提にしない。サイドカーは `PULLCEPT_ACCOUNT_ID` が無ければ キーごと省く。 あわせて `Account` に種別(`user` / `ai`)を足した。作成時の宣言であり、 接続の種類から推測しない——別クライアントから来た人間を、部屋は セッションと同じ種類の接続としてしか見られないためである。既存の config は `ai` を既定として読む。当時保存できたアカウントは全て CLI の起動 レシピだったので、これは fallback ではなく移行である。種別 `user` の アカウントの起動は `start_session` が拒否する。人間は起動するものでは ない。 #59
… a dialog 名簿と端末リストを一つにした。部屋が運ぶようになったアカウント id で 突き合わせているので、行は「誰が居るか」と「その相手に何ができるか」の 両方に答えられる。二つに割れていたのは `Participant` が id を持たず、 名前でしか結べなかったためである(#40 / #53 が却下した形)。 行は作成時に宣言した種別で分かれ、見出しが種別名と件数を示す(`user — 1` / `AI — 2`)。アカウントを持たない接続は宣言が無いので「ゲスト」に入る ——接続の種類から種別を推測しない。名前は参加者ごとの色で表示し、オフラ インは色を保ったまま薄くし、ドットを白抜きにする。色はアカウントの同一性 を担っているため、居ないだけで別人に見えてはならない。 行にアカウントの操作が乗る。編集はモーダルを開き、終了は #57 の 2 段階 (arm)をそのまま保つ。起動していないアカウントに端末の操作は出ない。 アカウントの作成・編集・削除をモーダルへ移した。欄の `change` では保存 しない。フォームは下書きを持ち、決定で初めてアカウント一覧へ入る。+ は 空のフォームを開くだけであり、取消は新規でも編集でも何も残さない。削除は 終了と同じ 2 クリックとし、`window.confirm` をやめた(#57 と同じ理由)。 画面の本人を種別 user のアカウントにした。#53 が保留にした判断であり、 本人が行を持てなかったことがリストの分裂の一因でもある。移行は `localStorage` の名前と色をそのアカウントの属性にするだけである。 タイトルバーの名前・色の欄は外した。同じ 2 値を編集する面が二つ並ぶのは、 この issue が閉じている分裂そのものだからである。 ランチャ行に残したのはアカウントの選択と参加のみ。名前・色・作業ディレク トリ・起動オプションと、実際に走る行の表示はモーダルへ移した。表示は 説明する欄の隣にあり、モーダルには行の無かった縦の余裕があるので、 省略記号で切らずに折り返す——切れるのはアプリが統合した channel エントリ であり、そこが見せる理由そのものである。 #59
要求仕様に反映した。部屋プロトコルへ `hello` / `room_join` の `account_id` と protocol 5 を足し、運ぶ理由(画面側の突き合わせ)と、 運ぶだけであること(名簿の鍵・自分の発言の抑止・`speaker` の刻印は接続 のまま)を分けて書いた。 参加者パネルの節を「リストは一つである」に書き直し、二つに割れていた 経緯と、種別ごとのグループ・件数・オフラインの見え方・行に乗る操作を 記した。端末の列の節はここへ畳んだ。 アカウントの節に種別を足し、「人間はアカウントではない」を「画面の本人は 種別 user のアカウントである」に置き換えた。#53 が保留にした判断であり、 「決まっていないこと」の表から外して「決まったこと」へ移した。モーダル (決定と取消、2 クリックの削除、ランチャ行に残すもの)も同節に書いた。 README も同じ範囲で直した。ウィンドウが開くこと、決定するまで保存され ないこと、リストが一つで種別ごとに分かれることを操作の順に書いている。 #59
本人のアカウントの種別を `user` から変えられないようにした。変えられると 本人が AI の見出しの下に並び、その名前で CLI を起動する選択肢が出る—— そういうものは一つも無い。 #59
smileygames
left a comment
There was a problem hiding this comment.
セルフレビュー(auto モード / 人間チェックなし)
完了条件の照合
| #59 の完了条件 | 結果 |
|---|---|
| リストが一つ | 満たす |
| 稼働中の行に 終了、arm を維持 | 満たす |
| 起動していないアカウントも起動直後から並ぶ | 満たす。config から描く |
| 種別で分かれ、数が読める | 満たす(user — 1 / AI — 2 / ゲスト — n) |
| 種別は作成時に選ぶ、推測しない | 満たす |
| 画面の本人が種別 user のアカウント | 満たす |
| モーダルで作成・編集・削除 | 満たす |
| モーダルに決定と取消、取消で何も残らない | 満たす。下記 |
| フォーカスが外れただけでは保存されない | 満たす |
| ランチャ行は選択と参加のみ | 満たす |
hello にアカウント id、Participant が持つ、protocol 5 |
満たす |
| id を持たない接続も参加者 | 満たす |
| 抑止と名簿の同一性が接続で判定されている | 満たす。下記 |
| グループ見出しが種別名と件数 | 満たす |
| オフラインが色を保ったまま薄く | 満たす(opacity 0.55、白抜きドット) |
「運ぶが判定しない」を親側で監査した
本 issue で最も壊れやすい制約なので、報告ではなくコードで確認した。
room.rs の account 出現箇所は全 30 行、いずれも保存・直列化・getter・正規化のみ。判定に使っている箇所は無い。同一性の四点はすべて接続のまま。
| 判定 | 実装 |
|---|---|
| 名簿の同一性 | participants: BTreeMap<String, Seat>(origin キー) |
| 自分か他人か | own: origin == room.local_origin |
| 自分の発言の抑止 | fanout.origin == own_origin |
speaker の刻印 |
joined_as(接続に紐づく) |
フロントの account === は 1 箇所、リストの突き合わせのみ(src/main.ts:594)。
コードが規律を明文化している点も評価する。
refusing a duplicate account belongs to the launcher (…) would be treating the account as the identity.
部屋が重複アカウントを拒否しないという判断まで書かれている。拒否は席(#53)の仕事であり、部屋がやればアカウントを同一性として扱ったことになる。制約の意図を理解している。
モーダルの決定/取消
draft はアカウント本体ではなく複製であり、決定 まで一切書かない。取消 は id すら消費しない。#59 が足した「モーダルにしただけでは直らない」部分が塞がっている。
種別 user の起動拒否
UI で隠すだけでなく session.rs で拒否している。理由もコードに書かれている。
Refused here rather than only hidden from the launcher, so the screen is not the only thing standing between a
useraccount and a spawnedclaude.
画面を唯一の防壁にしない形であり、妥当。
issue に無い判断(3 件、いずれも是認する)
- タイトルバーの 名前 / 色 を削除。 完了条件が本人を user アカウントにする以上、同じ対を編集する
change保存の面を残せば、この issue が閉じようとしている分裂を小さく再生産する。旧localStorageの値は一度だけ読んで移行し、以後書かない。妥当。 - アカウント id を持たない接続を「ゲスト」でまとめた。 種別ではなく種別の不在。代替は接続の種類からの推測であり、issue が禁じている。妥当。
- 削除を 2 段階 arm にし、
window.confirmをアプリから排除。 #57 の理由付け(ホストの応答に依存する二つの誤り方)がそのまま当たる。削除はモーダルへ移る過程で書き直されており、便乗ではない。妥当。
検証
CI(check / CI)いずれも 521831a で pass。親側で room.rs の account 全出現、同一性四点、protocol 定数、user 起動拒否、draft の意味論、リスト突き合わせを確認した。
実装側はスクリーンショットを取れない環境のため、計算済みスタイルで視覚条件を検証したと報告している。実機は未確認で、docs の未実装節に記載済み。阻却事由としない。
#60 への影響
タイトルバーから 名前 / 色 が消えた。#60(チャット文字サイズ)で「ヘッダは #59 と場所を取り合う」として置き場所を保留にしていたが、その競合は解消した。 #60 はヘッダに操作を置ける。
リリース種別
minor と判定する。protocol 4 → 5、画面構成の変更、アカウントに種別が加わる。構造変更かつ user/system observable。
自己レビュー通過。auto モードのため人間チェックはなく、このままマージへ進む。
Closes #59
名簿と端末リストを一つにし、行にアカウントの操作を載せた。種別ごとに分かれ、起動していないアカウントも最初から並ぶ。アカウントの作成・編集・削除は同一 webview 内のモーダルへ移し、決定と取消を持たせた。
変更の順序
353520b)—hello/room_joinに任意のaccount_idを足し、Accountにkind(user/ai)を足した。2bdf3a2)— 画面側。id が載ったことで名簿とアカウント一覧が結べる。c9e77e6)281ef4d)— リストを config だけから描く。521831a)— 本人のアカウントをuserから変えられないようにした。運ぶだけである
アカウント id は席に載って名簿で返るだけであり、判定には使っていない。自分の発言の抑止(
Fanout.origin)、名簿の鍵(RoomInner.participants)、Participant.own、speakerの刻印(joined_as)はいずれも接続のままである(#39 / #40 / #47)。id が無い接続も参加者として扱う——helloはPULLCEPT_ACCOUNT_IDが無ければキーごと省き、テストがその省略を検査している。判断を要する点
changeで保存する欄がタイトルバーに残るのは、この issue が閉じている分裂そのものになる。本人の名前と色は行の「編集」から直す。移行はlocalStorageの名前と色をその新しいアカウントの属性にするだけで、以後どちらのキーも書かない。userのアカウントをランチャの選択肢に出さない。start_sessionも拒否する(そちらが権威)。拒否が唯一の知る手段になるのを避けている。window.confirmをやめた。 feat(account): give each account its own terminal and a way to end it #57 が終了について書いた理由がそのまま当たる。window.confirmはこのアプリから消えた。対象外
ミュートと複数部屋は issue のとおり触っていない。
design/の canvas は #46 の担当であり、この PR では触っていない(差はさらに増えた)。確認
npm run build/npm run sidecar:check/npm run sidecar:test/cargo check/cargo test(mcp-config/room-floor)はローカルで通っている。リストとモーダルの見た目は dev サーバ上で light / dark 両方の算出値を確認した(名前が参加者の色で出ること、オフラインが色を保ったまま薄くなり点が輪郭になること、264px で横に溢れないこと、モーダルが viewport に収まること)。実機での操作は未確認であり、要求仕様の「未実装」節にそう書いた。🤖 Generated with Claude Code