diff --git a/CLAUDE.md b/CLAUDE.md index e5c605ccf..e6d36dc74 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -403,3 +403,54 @@ RecordUsage() / RecordPrompt() → in-memory のみ更新 - **クラッシュ後のリカバリ**: dirty データが失われた場合、セッション履歴は最後のチェックポイント以降が消える。ユーザーへの通知が必要か検討。 - **フラッシュ中の競合**: フラッシュgoroutineと `Save()` の同時呼び出しを防ぐため、既存の mutex を流用。 - **MEMORY.md のターンキャッシュ**: `edit_file` ツールが MEMORY.md を書き込んだ場合、**同ターン内のキャッシュを無効化**する仕組みが必要(`MemoryStore.InvalidateCache()` を edit_file のコールバックから呼ぶなど)。そうしないと同ターン内の後続の `GetPlanStatus()` などが古いキャッシュを読む。 + +### RAM が潤沢な場合の設計変更 + +対象デバイスは RAM 7GB / available 5GB 超(例: `free -m` で available ~5260MB)。 +この前提が上記の各戦略に与える影響を整理する。 + +#### 読み取りレイテンシの実態 + +`buff/cache` が 4.6GB 程度を占めるということは、OS のページキャッシュが空き RAM をほぼ全て使い切っている状態。`ReadLongTerm()` の複数回呼び出しは**実際にはディスクアクセスしていない**(2回目以降はページキャッシュヒット、マイクロ秒オーダー)。 + +読み取りの実コストは「ディスクI/O」ではなく「**syscall + 文字列 Split/Join のアロケーション**」。ターンスコープキャッシュの主な効果はレイテンシ削減より**GC 圧力の軽減**に変わる。 + +#### 書き込み寿命はRAMに影響されない + +書き込みは `O_SYNC` ではなくても `os.Rename` でアトミックに書かれるが、カーネルはライトバックキャッシュを経由して最終的に SD に書く。ページキャッシュが書き込みを吸収しても**最終的な NAND への書き込み回数は変わらない**。書き込み削減の優先度は変わらず高い。 + +#### インメモリ表現の常駐が現実的になる + +RAM が逼迫していない場合、`MemoryStore` に `*ParsedPlan` をフィールドとして持たせる設計(D-1, D-6 の解決策)のメモリコストは無視できる。MEMORY.md が数KB〜数十KB であっても、パース済み構造体として常駐させて差し支えない。 + +```go +// 設計案: MemoryStore がパース済み状態を保持 +type MemoryStore struct { + workspace string + memoryFile string + mu sync.RWMutex + cached *ParsedPlan // nil = 未ロード + cachedAt time.Time +} +// edit_file ツールが書き込んだ後に InvalidateCache() を呼ぶことで +// 同ターン内の再読み込みをトリガーできる +``` + +これにより `GetMemoryContext()` 内の `ReadLongTerm()` 多重呼び出し問題(D-1)と、 +public メソッドが `content` を隠す問題(D-6)が同時に解消される。 + +#### write-behind 窓をさらに広げられる + +RAM が十分にあるため、セッションデータを長時間インメモリに保持するリスクがない。 +write-behind の戦略を「5分 or 20件」から**「グレースフルシャットダウン時のみ + 30分タイマー」**に緩和しても、 +クラッシュ時の損失(最大30分の会話)と実装の単純さのトレードオフとして許容できる可能性がある。 +プロジェクトの可用性要件に応じて判断する。 + +#### 優先実装順の修正 + +RAM 制約がない前提での推奨順: + +1. **`stats.json` の write-behind** — 実装が最も単純(タイマー1本追加)、書き込み削減率が最大(98%) +2. **`sessions/*.json` の write-behind** — セッション単位の dirty フラグ + シャットダウンフック +3. **`MemoryStore` への `*ParsedPlan` 常駐** — D-1/D-6 を根本解決、読み取りアロケーションをゼロに +4. **ターンスコープキャッシュ** — 3 が実装されれば自然に解決するため不要になる可能性あり