feat(history): split the history into topics, resumable from the list (room_log, room.rs, session.rs, config.rs, sidecar, index.html, main.ts, styles.css, docs) - #116
Conversation
部屋のログを一本の流れから「一トピック一ファイル + 索引」へ移す。トピックは会話の記録ではなくセッションの器であり、開き直せばその会話が部屋に戻る。
- `logs/{room}/{topic}.jsonl` と `logs/{room}/index.json`。索引はトピック名・作成時刻・`{account_id: session_uuid}` を持つ(決定5)。
- ディレクトリが実在であり、索引はその注釈である。索引に無い `.jsonl` は読み出し時に拾う。これにより新規トピックの遅延生成が安全になる——起動のたびに空の行が索引へ残らず、投稿がファイルへ届いて索引へ届く前に落ちても、ファイルだけから索引を建て直せる。
- 起動時は新しいトピックを開く。索引へは書かない(Master 判断5、決定5から導かれること)。
- 既存の `logs/main.jsonl` は移動して一トピックとして引き継ぐ。捨てない(決定8)。
- トピック名は最初の発言の冒頭から自動生成し、後から変更できる(決定9)。
- `history` / `history_result` フレームを追加し protocol を 6 へ。参加者は現在のトピックの過去発言を自分で引ける。pull のみであり、部屋から push する経路は作らない(決定4C)。
- トピック切り替えで床を空にし、席の `since` を戻す。前のトピックの発言を「見落とし」として返す閉路を作らないため。
#115
… resume line
トピックが持つ `{account_id: session_uuid}` を起動へ差し込む。復帰の手段は二段構えであり、ここはネイティブ resume を持つ CLI の側(決定4B)。
- アカウントに `再開コマンド` を追加。`{session_id}` を含む一行であり、先頭語が command になる(例: `claude --resume {session_id}`)。
- 「こちらが UUID を決める」側は欄を増やさず、同じ `{session_id}` を起動オプションに書く形にした。どのフラグが id を運ぶかは CLI ごとの問いであり、再開コマンドが答えているのと同じ問いである。書かなければ id を配らない——resume を持たない CLI が恒常的に置かれる状態がこれである。
- 判定は「アカウントが resume を持つか」ではなく「このトピックがこのアカウントのセッションを持つか」で行う。どちらかが欠ければ新規起動であり、失敗ではない。読み出し(決定4C)が残るため。
- 起動チェック(`reject_incompatible_flags` / `declares_settings`)は解決後の行に対して走る。アカウントの行を見て別の行を spawn する形にしない。
- `StartedSession.resumed` を画面へ返す。トピックは復帰できた席とできなかった席の両方を抱えて開く(決定6)。
#115
`say_to_room` の隣に `read_room_history` を置く(決定4C)。部屋は過去を配らないままであり、変わるのは参加者が自分で取りに行けることだけ。#31 / #39 が退けた「部屋が誰の既読を持つ」形には触れない。 - `history` / `history_result` フレームで往復する。相関は `request_id`。投稿の相関が `message_id` であるのと同じ形。 - 既定 50 件、上限 200 件。`before` に message_id を渡すとその手前が返る。トピックに上限が無い以上、一回の tool 結果へ全部載せる形は採らない。 - 押し付けない。起動時に流し込む力業は、トピックが伸びるほど毎回の起動が高くつき、CLI は流し込まれた文を自分の入力と区別できず、端末ペインにその塊が見える(#84)。 - 「発言する道具は一本」の制約は投稿の軸のものであり、この道具は投稿しない。ラウンドトリップテストでその境界を書き直した。 - protocol 6。 #115
…o the room 左の列を「過去の発言を一本の流れで並べる帯」から「トピックの一覧」へ変える。求められていた形はこちらであり、#48 が埋めた粒度が外れていた。 - 一覧は新しい順。選ぶとそのトピックが部屋へ戻り、続きを話せる(決定2)。#48 の「過去ログを `#room` の本文へ復元しない」はこの issue で巻き戻る。 - `新規` が唯一の区切りである。起動も新しいトピックを開くが、二つは独立している(決定1、Master 判断5)。 - 起動時の現在トピックは索引に無い。一覧に無くても部屋のトピックとして描く——一件だけ「まだ書き留められていないトピック」が在るのが、毎回の起動直後の正しい状態である。 - トピック名はダブルクリックでその場で直す。Enter が決定、Escape とフォーカス喪失が取消(決定9)。 - 読み戻した行は部屋の行と同じ形で描き、透かしは消す。トピックへ入ると床は空になるため、前のトピックの `message_id` を申告しても位置 0 に落ちる。等価な値ではなく本当のことを言う。 - アカウントに `再開コマンド` の欄を追加し、起動オプションの `{session_id}` を説明した。 - 起動が resume だったかどうかを状況行で言う。復帰できなかった席も着席する以上、黙って同じ言葉で済ませない(決定6)。 #115
実装と同じ PR で要求仕様と README を更新する。 - 「部屋のログ」をトピック単位へ書き換え、「トピック」の節を新設した。器としてのトピック、遅延生成、索引とディレクトリの照合、二段構えの復帰、切り替え時の床の初期化。 - 「履歴」を「トピックの一覧」へ書き換えた。#48 の「過去ログを `#room` の本文へ復元しない」がここで巻き戻ることと、「起動時の部屋は新規である」が残ることを、別々に書いた。 - 部屋プロトコルの表へ `history` / `history_result` を足し、protocol 6 の由来を記した。pull であって push ではないことをフレームの節にも書いた。 - 「再開コマンド」の節を新設し、起動オプションの `{session_id}` を説明した。 - 受容したトレードオフに三件を足した。読み戻した行が宣言色を持たないこと、置換子を起動オプションへ書かせること、索引の照合が `cargo test` の外に在ること。 - 未実装へ、実機で辿っていない経路を並べた。`--session-id` で配った id が実際に `--resume` で戻るところ、エージェントが自分から読み出しを引くかどうか、既存ログの移行。 #115
…count 再開コマンドの分割結果が空かどうかを見ていたが、その条件は成立しない——空でない文字列を分割すると必ず一つ以上の語が出るためである。実際に通るのは引用符だけの行であり、そちらは空の語を一つ返す。空のまま command として渡せば、人が選んでいない名前で何も起動しない。 条件を「先頭の語が空か」へ移した。一つの条件で両方を覆う。文言も一行へ収めた(継続行の空白が畳まれて残っていた)。 #115
smileygames
left a comment
There was a problem hiding this comment.
自己レビュー(親 / auto モード)
brake 1 は適用外。rules/evolution/initiator-autonomy.md の self-evolution PR 定義は LI_PLUS_REPO のガバナンス面を変えることを条件②として要求し、本リポジトリは USER_REPO5 である。
merge は保留する。 auto モードに人間ゲートは無く、規約上この自己レビューの通過で merge できる。今回は親の判断で Master の確認を待つ。理由は #48 の顛末にある——本文の粒度が Master の意図と違っていたとき、実装も CI も self-review も「親が書いた本文」に照らして測るため、下流の誰も気づけなかった。この PR は同じ本文の書き手が同じ立場で書いており、同じ構造が残っている。
受け入れ基準の照合
| 決定 | 実装 | 結果 |
|---|---|---|
| 1 手動区切り | room_new_topic。起動と独立 |
pass |
| 2 再開できる | 一覧から選び #room へ読み戻す |
pass |
| 3 セッションの器 | Topic.sessions({account_id: session_uuid}、account id keyed) |
pass |
| 4B 再開コマンド | 再開コマンド フィールド+{session_id} 置換(crates/mcp-config、テスト4本) |
pass |
| 4C サイドカー pull | read_room_history。say_to_room は唯一の発話手段のまま |
pass |
| Master 5 起動時は新規トピック | RoomState::new が起動ごとに TopicRef を発行 |
pass |
| 遅延生成 | room_new_topic は何も書かない。索引と .jsonl は最初の投稿で生まれる |
pass |
| AI 5 ファイルの形 | logs/main/{topic}.jsonl + logs/main/index.json |
pass |
| AI 6 一部失敗でも開く | 復帰できない席は新規着席 | pass |
| AI 8 既存ログ移行 | 一トピックとして move(copy でも drop でもない) | pass |
| AI 9 トピック名 | 最初の投稿の冒頭から自動生成、編集可。TITLE_CHARS は文字数単位 |
pass |
設計として評価する点
- 実在をディレクトリ側に置き、索引を注釈とした。
read_indexが索引に無い.jsonlを拾うため、投稿がファイルに着いてから索引に着くまでのクラッシュ窓が閉じる。遅延生成の安全性がこれ一つで担保され、同じ仕組みが旧main.jsonlの移行も運んでいる。指示していない構造で、指示より良い。 Floor::resetは指示に無く、かつ load-bearing。 これが無いと、再開したトピックで最初に話した人が弾かれ、前のトピックの保持窓を「見逃し」として返される——実際には読んだ発言を、である。crates/room-floorにテストがある。topic_idは UUID で、タイトルとは別。 タイトルは編集されるため、名前に追随するファイル名は投稿を置き去りにする。人の入力がパスに届かない形にもなっている。declares_session_idが発行を門番している。 プレースホルダを書いていないアカウントには id を発行しない。発行だけして誰も渡さない id を索引が記録する状態を防ぐ。
スコープ
逸脱なし。仕様に無い判断3件は実装側が issue 本文の ### 実装時の確定(2026-08-27、実装 AI) に記録した。指示どおりの扱い。
規約面
PR タイトル純 ASCII(非 ASCII バイト 0)。PR 本文 Closes #115、日本語。コミット6本すべてタイトル純 ASCII、本文に日本語と #115 参照。docs 同 PR(docs/0-requirements.md / README.md)。CI 全コミット緑。
mergeStateStatus は実装側の報告時点で BLOCKED だったが、現在 CLEAN。main にブランチ保護は設定されておらず(gh api .../protection は 404)、必須レビューも無い。報告時点の CI 確定待ちを見たものと判断する。
持ち越し
- #114 はこの PR の決定5で解消するが、close は
Parent retains。merge 後に親が閉じる。 - 実機未検証。
--session-idで渡した id がclaude --resumeで実際に戻るか、参加者が必要な場面でread_room_historyを自分で引くか——どちらも観測でしか閉じない。「push しない」が「一度も pull されない」に潰れる形が残っている。docs/0-requirements.md未実装 に記載済み。
実機確認の結果(親 / 2026-08-27)
通ったもの
「トピックはセッションの器」が端まで成立している。 見つかった不具合 → #117セッションを起動するとその時点でトピックが索引に書かれるが、発言が無ければ
軽微な観測(issue 化せず記録のみ)
環境側の落とし穴(実装の不具合ではない)Claude セッションの内側から pullcept を起動すると 判定受け入れ基準は全て満たしている。#117 を分離した上で merge する。 |
Closes #115
履歴をトピック単位に分ける。左の列はトピックの一覧になり、選べばそのトピックが部屋へ戻って続きが話せる。トピックは会話の記録ではなくセッションの器であり、そこに居た CLI セッションが自分の文脈を持って戻る。
入ったもの
logs/main/{topic}.jsonlとlogs/main/index.json。索引はトピック名・作成時刻・{account_id: session_uuid}を持つ。実在はディレクトリ側にあり、索引に無い.jsonlは読み出し時に拾う——これで新規トピックの遅延生成が安全になる。新規を押して切る。起動も新しいトピックを開くが(Master 判断5)、二つは独立している。何も話さずに閉じた回は一覧に残らない。#roomの本文へ復元しない」はここで巻き戻り、「起動時の部屋は新規である」は残る。再開コマンドに{session_id}を書くと、トピックが持つ id が差し込まれる(B)。持たない CLI にはサイドカーのread_room_historyを与える(C)。履歴を PTY へ流し込む力業は採らない。logs/main.jsonlの移行(決定8)。移動であり、捨てない。触っていない一線
部屋から参加者へ過去を push しない。席を取る前の発言が届かないことは変わらず、#31 / #39 が退けた「部屋が誰の既読を持つ」形には入らない。読み出しは参加者が自分から引く pull のみである。
実装時に埋めた形
決定が形を指定していなかった箇所は issue 本文の「実装時の確定」に記録した。要点は二つ。
--session-id {uuid}相当」は欄を増やさず、同じ{session_id}置換子を起動オプションへ書く形にした。どのフラグが id を運ぶかは CLI ごとの問いであり、再開コマンドが答えているのと同じ問いである。確認
CI と同じものをローカルで通している:
npm run build/npm run sidecar:check/npm run sidecar:test/cargo check/crates/mcp-configとcrates/room-floorのcargo test。{session_id}の置換はcrates/mcp-configのテスト対象に入れ、トピック切り替え時の床の初期化はcrates/room-floorにテストを足した。サイドカーのラウンドトリップテストは pull の往復を辿る。実機での操作は未確認である。 実機で辿っていない経路は
docs/0-requirements.mdの「未実装」に並べた——--session-idで配った id が実際に--resumeで戻るところ、エージェントが必要な場面で自分から読み出しを引くかどうか、既存ログの移行が該当する。#114(起動時の全読み・全描画)は決定5の形で解消しているが、閉じる操作は行っていない。