状態をディスクに書いて、圧縮を生き延びる
「State that survives compression — because it's outside the conversation.」
TodoWrite と Task の違い
s03 の TodoManager もタスクを一覧にできる——しかしメモリに保存する。s06 で説明した auto_compact が発火し、messages[] が summary に置き換えられると、TodoManager の状態も一緒に消える(インメモリだから)。
s07 の Task は違う:各タスクは .tasks/task_42.json ファイルだ。context が圧縮されても・プロセスが再起動しても・agent が切り替わっても——ディスクのファイルが残っている限り、タスクは残っている。
# .tasks/task_12.json { "id": 12, "subject": "Refactor auth middleware", "description": "Extract JWT logic to shared module", "status": "pending", "blockedBy": [8, 11], # #8 と #11 の完了が先決 "owner": "" }
選択の基準:一時的なメモ(今回限りで忘れてよい)にはtodo、持続するタスク(セッションをまたいで保持・依存関係あり)にはtaskを使う。
blockedBy · 依存グラフの振る舞い
blockedBy はタスク id のリストだ——それらが全て completed になるまで、現在のタスクは「実行可能」にならない。
s07 の実装には一つ巧妙な細部がある:タスクを完了させると、そのタスクは他のすべてのタスクの blockedBy リストから自動的に除去される。
def _clear_dependency(self, completed_id: int): # Scan every task, remove completed_id from their blockedBy for f in self.dir.glob("task_*.json"): task = json.loads(f.read_text()) if completed_id in task.get("blockedBy", []): task["blockedBy"].remove(completed_id) self._save(task)
これにより agent は「何のタスクのブロックが解除されたか」を独立した表で管理する必要がない——.tasks/ を走査して status=="pending" and not blockedBy のものを探すだけで、今実行できるタスクが判明する。
依存グラフのインタラクション
以下の widget で5つのタスクの依存グラフを操作しよう。「complete」を押すと blockedBy が自動で更新され、「実行可能」になるタスクが緑でハイライトされる様子を確認できる。
圧縮後も生き残れるか
s06 で auto_compact が messages[] を一行の summary に置き換えることを説明した。しかしタスクはディスク上にあるため影響を受けない。確認してみよう:agent が5つのタスクを作成し、会話を圧縮した後で .tasks/ をスキャンすると、元の場所から作業を再開できる。