# Resume and History System - Implementation Plan ## Overview This document provides a detailed, actionable implementation plan for the Resume and History System. The system enables users to view historical conversation data, resume interrupted work, and rewind/restore to previous states in the Makima platform. **Key Reference Documents:** - Specification: Resume and History System Specification - Requirements: Requirements Document - User Stories: User Stories Document --- ## Phase 1: Database Schema **Objective:** Create the foundational database structures for storing conversation snapshots, history events, and supporting task forking. ### Task 1.1: Create Database Migrations **Files to Create:** - `makima/migrations/20250117000000_history_tables.sql` **Schema Changes:** ```sql -- 1. Conversation Snapshots table -- Stores conversation state at specific points for rewind capability CREATE TABLE IF NOT EXISTS conversation_snapshots ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, checkpoint_id UUID REFERENCES task_checkpoints(id) ON DELETE SET NULL, snapshot_type VARCHAR(50) NOT NULL, -- 'auto', 'manual', 'checkpoint' message_count INTEGER NOT NULL, conversation_state JSONB NOT NULL, -- Full conversation at this point metadata JSONB, -- Additional context (token count, cost, etc.) created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_conversation_snapshots_task ON conversation_snapshots(task_id); CREATE INDEX idx_conversation_snapshots_checkpoint ON conversation_snapshots(checkpoint_id); CREATE INDEX idx_conversation_snapshots_created ON conversation_snapshots(created_at DESC); -- 2. History Events table -- Unified event stream for timeline views CREATE TABLE IF NOT EXISTS history_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, contract_id UUID REFERENCES contracts(id) ON DELETE CASCADE, task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, event_type VARCHAR(50) NOT NULL, -- 'task', 'chat', 'checkpoint', 'phase', 'file' event_subtype VARCHAR(50), -- Specific event: 'created', 'completed', 'message', etc. phase VARCHAR(50), -- Contract phase when event occurred event_data JSONB NOT NULL, -- Event-specific data created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_history_events_contract ON history_events(contract_id, created_at DESC); CREATE INDEX idx_history_events_task ON history_events(task_id, created_at DESC); CREATE INDEX idx_history_events_owner ON history_events(owner_id, created_at DESC); CREATE INDEX idx_history_events_type ON history_events(event_type, created_at DESC); -- 3. Alter task_checkpoints - add conversation snapshot reference ALTER TABLE task_checkpoints ADD COLUMN conversation_snapshot_id UUID REFERENCES conversation_snapshots(id) ON DELETE SET NULL; -- 4. Alter tasks - add forking fields ALTER TABLE tasks ADD COLUMN forked_from_task_id UUID REFERENCES tasks(id) ON DELETE SET NULL, ADD COLUMN forked_at_checkpoint_id UUID REFERENCES task_checkpoints(id) ON DELETE SET NULL; CREATE INDEX idx_tasks_forked_from ON tasks(forked_from_task_id) WHERE forked_from_task_id IS NOT NULL; -- Comments for documentation COMMENT ON TABLE conversation_snapshots IS 'Stores conversation state at specific points for rewind/resume capability'; COMMENT ON TABLE history_events IS 'Unified event stream for timeline views across contracts and tasks'; COMMENT ON COLUMN conversation_snapshots.snapshot_type IS 'Type: auto (periodic), manual (user-triggered), checkpoint (at git checkpoint)'; COMMENT ON COLUMN history_events.event_type IS 'Category: task, chat, checkpoint, phase, file'; ``` **Complexity:** Medium **Dependencies:** None **Estimated Time:** 2-3 hours --- ## Phase 2: Repository Layer **Objective:** Implement database access functions for conversation snapshots, history events, and enhanced checkpoint operations. ### Task 2.1: Core Models **Files to Modify:** - `makima/src/db/models.rs` **New Types to Add:** ```rust // ConversationSnapshot model #[derive(Debug, Clone, FromRow, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ConversationSnapshot { pub id: Uuid, pub task_id: Uuid, pub checkpoint_id: Option, pub snapshot_type: String, // 'auto', 'manual', 'checkpoint' pub message_count: i32, #[sqlx(json)] pub conversation_state: serde_json::Value, #[sqlx(json)] pub metadata: Option, pub created_at: DateTime, } // HistoryEvent model #[derive(Debug, Clone, FromRow, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct HistoryEvent { pub id: Uuid, pub owner_id: Uuid, pub contract_id: Option, pub task_id: Option, pub event_type: String, pub event_subtype: Option, pub phase: Option, #[sqlx(json)] pub event_data: serde_json::Value, pub created_at: DateTime, } // Unified ConversationMessage for API responses #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ConversationMessage { pub id: String, pub role: String, // 'user', 'assistant', 'system', 'tool' pub content: String, pub timestamp: DateTime, #[serde(skip_serializing_if = "Option::is_none")] pub tool_calls: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub tool_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tool_input: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tool_result: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_error: Option, #[serde(skip_serializing_if = "Option::is_none")] pub token_count: Option, #[serde(skip_serializing_if = "Option::is_none")] pub cost_usd: Option, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ToolCallInfo { pub id: String, pub name: String, pub input: serde_json::Value, } // Query filters for history #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct HistoryQueryFilters { pub phase: Option, pub event_types: Option>, pub from: Option>, pub to: Option>, pub limit: Option, pub cursor: Option, } // Resume request types #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ResumeSupervisorRequest { pub target_daemon_id: Option, pub resume_mode: String, // 'continue', 'restart_phase', 'from_checkpoint' pub checkpoint_id: Option, pub additional_context: Option, } #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ResumeFromCheckpointRequest { pub task_name: Option, pub plan: String, pub include_conversation: Option, pub target_daemon_id: Option, } // Rewind request types #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct RewindTaskRequest { pub checkpoint_id: Option, pub checkpoint_sha: Option, pub preserve_mode: String, // 'discard', 'create_branch', 'stash' pub branch_name: Option, } #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct RewindConversationRequest { pub to_message_id: Option, pub to_timestamp: Option>, pub by_message_count: Option, pub rewind_code: Option, } // Fork request type #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ForkTaskRequest { pub fork_from_type: String, // 'checkpoint', 'timestamp', 'message_id' pub fork_from_value: String, pub new_task_name: String, pub new_task_plan: String, pub include_conversation: Option, pub create_branch: Option, pub branch_name: Option, } ``` **Complexity:** Medium **Dependencies:** Phase 1 complete **Estimated Time:** 2-3 hours ### Task 2.2: Repository Functions **Files to Modify:** - `makima/src/db/repository.rs` **Functions to Add:** ```rust // ============================================================================ // Conversation Snapshots // ============================================================================ /// Create a new conversation snapshot pub async fn create_conversation_snapshot( pool: &PgPool, task_id: Uuid, checkpoint_id: Option, snapshot_type: &str, message_count: i32, conversation_state: serde_json::Value, metadata: Option, ) -> Result /// Get a conversation snapshot by ID pub async fn get_conversation_snapshot( pool: &PgPool, id: Uuid, ) -> Result, sqlx::Error> /// Get conversation snapshot at a specific checkpoint pub async fn get_conversation_at_checkpoint( pool: &PgPool, checkpoint_id: Uuid, ) -> Result, sqlx::Error> /// List conversation snapshots for a task pub async fn list_conversation_snapshots( pool: &PgPool, task_id: Uuid, limit: Option, ) -> Result, sqlx::Error> /// Delete conversation snapshots older than retention period pub async fn cleanup_old_snapshots( pool: &PgPool, retention_days: i32, ) -> Result // ============================================================================ // History Events // ============================================================================ /// Record a new history event pub async fn record_history_event( pool: &PgPool, owner_id: Uuid, contract_id: Option, task_id: Option, event_type: &str, event_subtype: Option<&str>, phase: Option<&str>, event_data: serde_json::Value, ) -> Result /// Get contract history timeline pub async fn get_contract_history( pool: &PgPool, contract_id: Uuid, owner_id: Uuid, filters: &HistoryQueryFilters, ) -> Result<(Vec, i64), sqlx::Error> /// Get task history pub async fn get_task_history( pool: &PgPool, task_id: Uuid, owner_id: Uuid, filters: &HistoryQueryFilters, ) -> Result<(Vec, i64), sqlx::Error> /// Get unified timeline for an owner pub async fn get_timeline( pool: &PgPool, owner_id: Uuid, filters: &HistoryQueryFilters, ) -> Result<(Vec, i64), sqlx::Error> // ============================================================================ // Task Conversation Retrieval // ============================================================================ /// Get task conversation messages (reconstructed from task_events) pub async fn get_task_conversation( pool: &PgPool, task_id: Uuid, include_tool_calls: bool, include_tool_results: bool, limit: Option, ) -> Result, sqlx::Error> /// Get supervisor conversation (from supervisor_states) pub async fn get_supervisor_conversation( pool: &PgPool, contract_id: Uuid, ) -> Result)>, sqlx::Error> // ============================================================================ // Checkpoint Operations // ============================================================================ /// Create checkpoint with conversation snapshot pub async fn create_checkpoint_with_snapshot( pool: &PgPool, task_id: Uuid, checkpoint_number: i32, commit_sha: &str, branch_name: &str, message: &str, files_changed: Option, lines_added: Option, lines_removed: Option, conversation_state: serde_json::Value, ) -> Result<(TaskCheckpoint, ConversationSnapshot), sqlx::Error> /// Get checkpoint diff information (requires daemon interaction for actual diff) pub async fn get_checkpoint_info( pool: &PgPool, checkpoint_id: Uuid, ) -> Result)>, sqlx::Error> // ============================================================================ // Fork Operations // ============================================================================ /// Create forked task pub async fn create_forked_task( pool: &PgPool, owner_id: Uuid, source_task_id: Uuid, checkpoint_id: Option, req: &CreateTaskRequest, ) -> Result ``` **Complexity:** Complex **Dependencies:** Task 2.1 complete **Estimated Time:** 4-5 hours --- ## Phase 3: API Endpoints **Objective:** Implement REST API endpoints for history viewing, resume operations, and rewind/fork functionality. ### Task 3.1: History Endpoints **Files to Create/Modify:** - `makima/src/server/handlers/history.rs` (new file) - `makima/src/server/handlers/mod.rs` (add module) - `makima/src/server/mod.rs` (add routes) **Endpoints to Implement:** | Method | Path | Handler Function | |--------|------|------------------| | GET | `/api/v1/contracts/{id}/history` | `get_contract_history` | | GET | `/api/v1/contracts/{id}/supervisor/conversation` | `get_supervisor_conversation` | | GET | `/api/v1/mesh/tasks/{id}/conversation` | `get_task_conversation` | | GET | `/api/v1/mesh/tasks/{id}/checkpoints/{cid}/diff` | `get_checkpoint_diff` | | GET | `/api/v1/timeline` | `get_timeline` | **Implementation Details:** ```rust // makima/src/server/handlers/history.rs /// GET /api/v1/contracts/{id}/history /// Returns contract history timeline with filtering and pagination #[utoipa::path( get, path = "/api/v1/contracts/{id}/history", params( ("id" = Uuid, Path, description = "Contract ID"), HistoryQueryFilters ), responses( (status = 200, body = ContractHistoryResponse), (status = 404, description = "Contract not found"), ), tag = "history" )] pub async fn get_contract_history( State(state): State, Path(contract_id): Path, Query(filters): Query, auth: AuthenticatedUser, ) -> Result, ApiError> /// GET /api/v1/contracts/{id}/supervisor/conversation /// Returns full supervisor conversation with spawned task references #[utoipa::path( get, path = "/api/v1/contracts/{id}/supervisor/conversation", responses( (status = 200, body = SupervisorConversationResponse), (status = 404, description = "Supervisor not found"), ), tag = "history" )] pub async fn get_supervisor_conversation( State(state): State, Path(contract_id): Path, auth: AuthenticatedUser, ) -> Result, ApiError> /// GET /api/v1/mesh/tasks/{id}/conversation /// Returns task conversation history #[utoipa::path( get, path = "/api/v1/mesh/tasks/{id}/conversation", params( ("id" = Uuid, Path, description = "Task ID"), ("include_tool_calls" = Option, Query), ("include_tool_results" = Option, Query), ("limit" = Option, Query), ), responses( (status = 200, body = TaskConversationResponse), (status = 404, description = "Task not found"), ), tag = "history" )] pub async fn get_task_conversation( State(state): State, Path(task_id): Path, Query(params): Query, auth: AuthenticatedUser, ) -> Result, ApiError> /// GET /api/v1/mesh/tasks/{id}/checkpoints/{cid}/diff /// Returns checkpoint diff (delegates to daemon for git diff) pub async fn get_checkpoint_diff( State(state): State, Path((task_id, checkpoint_id)): Path<(Uuid, Uuid)>, auth: AuthenticatedUser, ) -> Result, ApiError> /// GET /api/v1/timeline /// Returns unified timeline for authenticated user pub async fn get_timeline( State(state): State, Query(filters): Query, auth: AuthenticatedUser, ) -> Result, ApiError> ``` **Complexity:** Medium **Dependencies:** Phase 2 complete **Estimated Time:** 4-5 hours ### Task 3.2: Resume Endpoints **Files to Modify:** - `makima/src/server/handlers/mesh_supervisor.rs` - `makima/src/server/handlers/mesh.rs` **Endpoints to Implement:** | Method | Path | Handler Function | |--------|------|------------------| | POST | `/api/v1/contracts/{id}/supervisor/resume` | `resume_supervisor` | | POST | `/api/v1/mesh/tasks/{id}/checkpoints/{cid}/resume` | `resume_from_checkpoint` | | POST | `/api/v1/mesh/tasks/{id}/continue` | `continue_task` (enhanced) | **Implementation Details:** ```rust // makima/src/server/handlers/mesh_supervisor.rs /// POST /api/v1/contracts/{id}/supervisor/resume /// Resume interrupted supervisor with specified mode #[utoipa::path( post, path = "/api/v1/contracts/{id}/supervisor/resume", request_body = ResumeSupervisorRequest, responses( (status = 200, body = ResumeSupervisorResponse), (status = 404, description = "Contract/supervisor not found"), (status = 409, description = "Supervisor already running"), ), tag = "supervisor" )] pub async fn resume_supervisor( State(state): State, Path(contract_id): Path, auth: AuthenticatedUser, Json(req): Json, ) -> Result, ApiError> // makima/src/server/handlers/mesh.rs /// POST /api/v1/mesh/tasks/{id}/checkpoints/{cid}/resume /// Create new task starting from specific checkpoint #[utoipa::path( post, path = "/api/v1/mesh/tasks/{id}/checkpoints/{cid}/resume", request_body = ResumeFromCheckpointRequest, responses( (status = 201, body = ResumeFromCheckpointResponse), (status = 404, description = "Task/checkpoint not found"), ), tag = "mesh" )] pub async fn resume_from_checkpoint( State(state): State, Path((task_id, checkpoint_id)): Path<(Uuid, Uuid)>, auth: AuthenticatedUser, Json(req): Json, ) -> Result, ApiError> /// POST /api/v1/mesh/tasks/{id}/continue (enhanced) /// Enhanced with resume mode and context options pub async fn continue_task( State(state): State, Path(task_id): Path, auth: AuthenticatedUser, Json(req): Json, // Enhanced request type ) -> Result, ApiError> ``` **Complexity:** Complex **Dependencies:** Task 3.1 complete **Estimated Time:** 5-6 hours ### Task 3.3: Rewind and Fork Endpoints **Files to Modify:** - `makima/src/server/handlers/mesh.rs` - `makima/src/server/handlers/mesh_supervisor.rs` **Endpoints to Implement:** | Method | Path | Handler Function | |--------|------|------------------| | POST | `/api/v1/mesh/tasks/{id}/rewind` | `rewind_task` | | POST | `/api/v1/contracts/{id}/supervisor/conversation/rewind` | `rewind_conversation` | | POST | `/api/v1/mesh/tasks/{id}/fork` | `fork_task` | | POST | `/api/v1/mesh/tasks/{id}/checkpoints/{cid}/branch` | `branch_from_checkpoint` | **Implementation Details:** ```rust // makima/src/server/handlers/mesh.rs /// POST /api/v1/mesh/tasks/{id}/rewind /// Rewind task code to specified checkpoint #[utoipa::path( post, path = "/api/v1/mesh/tasks/{id}/rewind", request_body = RewindTaskRequest, responses( (status = 200, body = RewindTaskResponse), (status = 404, description = "Task/checkpoint not found"), (status = 409, description = "Task is running"), ), tag = "mesh" )] pub async fn rewind_task( State(state): State, Path(task_id): Path, auth: AuthenticatedUser, Json(req): Json, ) -> Result, ApiError> /// POST /api/v1/mesh/tasks/{id}/fork /// Fork task from historical point #[utoipa::path( post, path = "/api/v1/mesh/tasks/{id}/fork", request_body = ForkTaskRequest, responses( (status = 201, body = ForkTaskResponse), (status = 404, description = "Task not found"), ), tag = "mesh" )] pub async fn fork_task( State(state): State, Path(task_id): Path, auth: AuthenticatedUser, Json(req): Json, ) -> Result, ApiError> /// POST /api/v1/mesh/tasks/{id}/checkpoints/{cid}/branch /// Create git branch from checkpoint without starting task pub async fn branch_from_checkpoint( State(state): State, Path((task_id, checkpoint_id)): Path<(Uuid, Uuid)>, auth: AuthenticatedUser, Json(req): Json, ) -> Result, ApiError> // makima/src/server/handlers/mesh_supervisor.rs /// POST /api/v1/contracts/{id}/supervisor/conversation/rewind /// Rewind supervisor conversation to specified point pub async fn rewind_conversation( State(state): State, Path(contract_id): Path, auth: AuthenticatedUser, Json(req): Json, ) -> Result, ApiError> ``` **Complexity:** Complex **Dependencies:** Task 3.2 complete **Estimated Time:** 5-6 hours --- ## Phase 4: CLI Commands **Objective:** Implement command-line interface for history viewing, resume, and rewind operations. ### Task 4.1: History Commands **Files to Modify:** - `makima/src/daemon/cli/mod.rs` - `makima/src/daemon/cli/contract.rs` - `makima/src/daemon/cli/supervisor.rs` **New Commands:** ```rust // makima/src/daemon/cli/mod.rs /// Contract subcommands - add history commands #[derive(Subcommand, Debug)] pub enum ContractCommand { // ... existing commands ... /// View contract history timeline History(contract::HistoryArgs), } /// Supervisor subcommands - add history and resume commands #[derive(Subcommand, Debug)] pub enum SupervisorCommand { // ... existing commands ... /// View task conversation history TaskHistory(supervisor::TaskHistoryArgs), /// List checkpoints with details CheckpointList(supervisor::CheckpointListArgs), /// View checkpoint diff CheckpointDiff(supervisor::CheckpointDiffArgs), /// Resume supervisor after interruption Resume(supervisor::ResumeArgs), } ``` **Implementation - Contract History:** ```rust // makima/src/daemon/cli/contract.rs #[derive(Args, Debug)] pub struct HistoryArgs { /// Filter by phase (research, specify, plan, execute, review) #[arg(long)] pub phase: Option, /// Filter from date (ISO 8601 format) #[arg(long)] pub from: Option, /// Filter to date (ISO 8601 format) #[arg(long)] pub to: Option, /// Maximum entries to return #[arg(long, default_value = "50")] pub limit: i32, /// Output format (table, json) #[arg(long, default_value = "table")] pub format: String, } pub async fn handle_history(args: &HistoryArgs) -> Result<()> { // 1. Get contract context from environment // 2. Call /api/v1/contracts/{id}/history with filters // 3. Format and display results } ``` **Implementation - Task History:** ```rust // makima/src/daemon/cli/supervisor.rs #[derive(Args, Debug)] pub struct TaskHistoryArgs { /// Task ID to view history for pub task_id: Uuid, /// Include tool calls in output #[arg(long, default_value = "true")] pub tool_calls: bool, /// Maximum messages to return #[arg(long)] pub limit: Option, /// Output format (table, json, chat) #[arg(long, default_value = "chat")] pub format: String, } pub async fn handle_task_history(args: &TaskHistoryArgs) -> Result<()> { // 1. Call /api/v1/mesh/tasks/{id}/conversation // 2. Format as chat-style output or JSON } #[derive(Args, Debug)] pub struct CheckpointListArgs { /// Task ID to list checkpoints for pub task_id: Uuid, /// Include diff summary #[arg(long)] pub with_diff: bool, } #[derive(Args, Debug)] pub struct CheckpointDiffArgs { /// Task ID pub task_id: Uuid, /// Checkpoint number pub checkpoint_number: i32, } ``` **Complexity:** Medium **Dependencies:** Phase 3 complete **Estimated Time:** 3-4 hours ### Task 4.2: Resume and Rewind Commands **Files to Modify:** - `makima/src/daemon/cli/supervisor.rs` - `makima/src/daemon/cli/contract.rs` **New Commands:** ```rust // makima/src/daemon/cli/supervisor.rs #[derive(Args, Debug)] pub struct ResumeArgs { /// Resume mode: continue, restart_phase, from_checkpoint #[arg(long, default_value = "continue")] pub mode: String, /// Checkpoint ID (required for from_checkpoint mode) #[arg(long)] pub checkpoint: Option, /// Additional context to inject #[arg(long)] pub context: Option, } pub async fn handle_resume(args: &ResumeArgs) -> Result<()> { // 1. Get contract context // 2. Call /api/v1/contracts/{id}/supervisor/resume // 3. Display result and new supervisor task info } #[derive(Args, Debug)] pub struct TaskResumeArgs { /// Task ID to resume pub task_id: Uuid, /// Resume mode: with_context, clean_restart, from_checkpoint #[arg(long, default_value = "with_context")] pub mode: String, /// Checkpoint SHA (for from_checkpoint mode) #[arg(long)] pub checkpoint: Option, } #[derive(Args, Debug)] pub struct TaskResumeFromArgs { /// Source task ID pub task_id: Uuid, /// Checkpoint number to resume from #[arg(long)] pub checkpoint: i32, /// Plan for the new task #[arg(long)] pub plan: String, /// Name for the new task #[arg(long)] pub name: Option, } #[derive(Args, Debug)] pub struct TaskRewindArgs { /// Task ID to rewind pub task_id: Uuid, /// Checkpoint number to rewind to #[arg(long)] pub checkpoint: i32, /// Preserve mode: discard, create_branch, stash #[arg(long, default_value = "create_branch")] pub preserve: String, /// Branch name (for create_branch mode) #[arg(long)] pub branch_name: Option, } #[derive(Args, Debug)] pub struct TaskForkArgs { /// Source task ID pub task_id: Uuid, /// Checkpoint number to fork from #[arg(long)] pub checkpoint: i32, /// Name for the new task #[arg(long)] pub name: String, /// Plan for the new task #[arg(long)] pub plan: String, /// Include conversation history #[arg(long, default_value = "true")] pub include_conversation: bool, } #[derive(Args, Debug)] pub struct ConversationRewindArgs { /// Number of messages to rewind #[arg(long)] pub by_messages: Option, /// Message ID to rewind to #[arg(long)] pub to_message: Option, /// Also rewind code to matching checkpoint #[arg(long)] pub rewind_code: bool, } ``` **Update CLI Commands enum:** ```rust // makima/src/daemon/cli/mod.rs #[derive(Subcommand, Debug)] pub enum SupervisorCommand { // ... existing commands ... /// Resume supervisor after interruption Resume(supervisor::ResumeArgs), /// Resume task with context TaskResume(supervisor::TaskResumeArgs), /// Resume from specific checkpoint TaskResumeFrom(supervisor::TaskResumeFromArgs), /// Rewind task code to checkpoint TaskRewind(supervisor::TaskRewindArgs), /// Fork task from historical point TaskFork(supervisor::TaskForkArgs), /// Rewind supervisor conversation RewindConversation(supervisor::ConversationRewindArgs), } ``` **Complexity:** Medium **Dependencies:** Task 4.1 complete **Estimated Time:** 4-5 hours --- ## Phase 5: Daemon Integration **Objective:** Extend daemon protocol and handlers to support rewind operations and conversation snapshots. ### Task 5.1: Protocol Extensions **Files to Modify:** - `makima/src/daemon/ws/protocol.rs` - `makima/src/server/protocol.rs` (if separate) **New Commands to Add:** ```rust // makima/src/daemon/ws/protocol.rs /// Command from server to daemon #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "camelCase")] pub enum DaemonCommand { // ... existing commands ... /// Rewind task worktree to a specific checkpoint RewindToCheckpoint { #[serde(rename = "taskId")] task_id: Uuid, #[serde(rename = "checkpointSha")] checkpoint_sha: String, /// How to preserve current state: 'discard', 'create_branch', 'stash' #[serde(rename = "preserveMode")] preserve_mode: String, /// Branch name for create_branch mode #[serde(rename = "branchName")] branch_name: Option, }, /// Create a conversation snapshot for a task CreateConversationSnapshot { #[serde(rename = "taskId")] task_id: Uuid, }, /// Get git diff between two commits GetCheckpointDiff { #[serde(rename = "taskId")] task_id: Uuid, /// SHA of the checkpoint to diff #[serde(rename = "checkpointSha")] checkpoint_sha: String, /// SHA of the previous checkpoint (for comparison) #[serde(rename = "previousSha")] previous_sha: Option, }, } /// Message from daemon to server #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "camelCase")] pub enum DaemonMessage { // ... existing messages ... /// Response to RewindToCheckpoint command RewindResult { #[serde(rename = "taskId")] task_id: Uuid, success: bool, message: String, /// Reference to preserved state (branch name or stash ref) #[serde(rename = "preservedAs")] preserved_as: Option, }, /// Response to CreateConversationSnapshot command ConversationSnapshotCreated { #[serde(rename = "taskId")] task_id: Uuid, #[serde(rename = "snapshotId")] snapshot_id: Uuid, #[serde(rename = "messageCount")] message_count: i32, }, /// Response to GetCheckpointDiff command CheckpointDiffResult { #[serde(rename = "taskId")] task_id: Uuid, success: bool, diff: Option, error: Option, }, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PreservedState { /// Type: 'branch' or 'stash' #[serde(rename = "type")] pub state_type: String, /// Branch name or stash reference pub reference: String, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GitDiffInfo { pub files: Vec, pub stats: DiffStats, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FileDiffInfo { pub path: String, /// Action: 'added', 'modified', 'deleted' pub action: String, pub additions: i32, pub deletions: i32, /// Diff hunks (optional, may be truncated for large files) pub hunks: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DiffHunk { pub header: String, pub lines: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DiffStats { pub files_changed: i32, pub insertions: i32, pub deletions: i32, } ``` **Complexity:** Medium **Dependencies:** None (can be done in parallel with Phase 3-4) **Estimated Time:** 2-3 hours ### Task 5.2: Daemon Handlers **Files to Modify:** - `makima/src/daemon/task/manager.rs` - `makima/src/daemon/worktree/manager.rs` **Handlers to Implement:** ```rust // makima/src/daemon/task/manager.rs impl TaskManager { /// Handle rewind to checkpoint command pub async fn handle_rewind_to_checkpoint( &mut self, task_id: Uuid, checkpoint_sha: &str, preserve_mode: &str, branch_name: Option<&str>, ) -> Result { // 1. Validate task exists and is not running // 2. Get worktree path for task // 3. Based on preserve_mode: // - 'discard': git reset --hard {sha} // - 'create_branch': git branch {name} && git reset --hard {sha} // - 'stash': git stash && git reset --hard {sha} // 4. Return result with preserved reference } /// Handle conversation snapshot creation pub async fn handle_create_conversation_snapshot( &mut self, task_id: Uuid, ) -> Result { // 1. Get task state including conversation // 2. Send to server for storage // 3. Return snapshot ID } /// Handle checkpoint diff request pub async fn handle_get_checkpoint_diff( &mut self, task_id: Uuid, checkpoint_sha: &str, previous_sha: Option<&str>, ) -> Result { // 1. Get worktree path for task // 2. Run git diff command // 3. Parse and return diff info } } ``` **Files to Modify:** - `makima/src/daemon/worktree/manager.rs` **Helper Functions:** ```rust // makima/src/daemon/worktree/manager.rs impl WorktreeManager { /// Reset worktree to specific commit pub async fn reset_to_commit( &self, worktree_path: &Path, commit_sha: &str, ) -> Result<(), WorktreeError> /// Create branch from current state pub async fn create_branch( &self, worktree_path: &Path, branch_name: &str, ) -> Result<(), WorktreeError> /// Stash current changes pub async fn stash_changes( &self, worktree_path: &Path, ) -> Result // Returns stash ref /// Get diff between two commits pub async fn get_diff( &self, worktree_path: &Path, from_sha: &str, to_sha: &str, ) -> Result /// Parse git diff output into structured format pub fn parse_diff(diff_output: &str) -> Result } ``` **Complexity:** Complex **Dependencies:** Task 5.1 complete **Estimated Time:** 4-5 hours ### Task 5.3: Checkpoint Enhancement **Files to Modify:** - `makima/src/daemon/task/manager.rs` - `makima/src/server/handlers/mesh_daemon.rs` **Changes:** 1. **Update checkpoint creation to include conversation state:** ```rust // When creating a checkpoint, also capture current conversation pub async fn create_checkpoint_with_conversation( &mut self, task_id: Uuid, message: &str, ) -> Result<(TaskCheckpoint, ConversationSnapshot), DaemonError> { // 1. Create git commit (existing logic) // 2. Capture current conversation state // 3. Create checkpoint record with snapshot reference } ``` 2. **Update task spawning to handle fork/resume scenarios:** ```rust // When spawning task with forked_from_task_id or checkpoint_sha: // - If checkpoint_sha: create worktree at that specific commit // - If include_conversation: inject conversation history into plan ``` **Complexity:** Medium **Dependencies:** Task 5.2 complete **Estimated Time:** 3-4 hours --- ## Implementation Summary ### Phase Dependencies ``` Phase 1 (Database) └─> Phase 2 (Repository) └─> Phase 3 (API Endpoints) └─> Phase 4 (CLI Commands) Phase 5 (Daemon) can be done in parallel with Phases 3-4 ``` ### Estimated Total Time | Phase | Tasks | Estimated Hours | |-------|-------|-----------------| | Phase 1: Database Schema | 1 task | 2-3 hours | | Phase 2: Repository Layer | 2 tasks | 6-8 hours | | Phase 3: API Endpoints | 3 tasks | 14-17 hours | | Phase 4: CLI Commands | 2 tasks | 7-9 hours | | Phase 5: Daemon Integration | 3 tasks | 9-12 hours | | **Total** | **11 tasks** | **38-49 hours** | ### Task Breakdown by Complexity | Complexity | Count | Tasks | |------------|-------|-------| | Simple | 1 | Phase 1 Migration | | Medium | 6 | Models, History Endpoints, CLI History, CLI Resume, Protocol, Checkpoint Enhancement | | Complex | 4 | Repository Functions, Resume Endpoints, Rewind/Fork Endpoints, Daemon Handlers | ### Files to Create 1. `makima/migrations/20250117000000_history_tables.sql` 2. `makima/src/server/handlers/history.rs` ### Files to Modify **Database Layer:** - `makima/src/db/models.rs` - `makima/src/db/repository.rs` **API Layer:** - `makima/src/server/handlers/mod.rs` - `makima/src/server/handlers/mesh.rs` - `makima/src/server/handlers/mesh_supervisor.rs` - `makima/src/server/mod.rs` **CLI Layer:** - `makima/src/daemon/cli/mod.rs` - `makima/src/daemon/cli/contract.rs` - `makima/src/daemon/cli/supervisor.rs` **Daemon Layer:** - `makima/src/daemon/ws/protocol.rs` - `makima/src/daemon/task/manager.rs` - `makima/src/daemon/worktree/manager.rs` - `makima/src/server/handlers/mesh_daemon.rs` --- ## Testing Strategy ### Unit Tests - Repository functions: CRUD operations for snapshots and events - Model serialization/deserialization - Diff parsing functions ### Integration Tests - API endpoint tests with mock database - CLI command execution tests - Daemon command handling tests ### End-to-End Tests - Complete workflow: Create task -> Checkpoint -> Rewind -> Resume - Fork workflow: Create task -> Checkpoint -> Fork -> Verify state - History viewing across contract lifecycle ### Manual Testing Checklist - [ ] Create conversation snapshot at checkpoint - [ ] View contract history timeline with filters - [ ] View task conversation history - [ ] Resume interrupted supervisor (all modes) - [ ] Resume task from specific checkpoint - [ ] Rewind code with branch preservation - [ ] Rewind conversation to message - [ ] Fork task from checkpoint - [ ] CLI commands output correctly formatted --- ## Security Considerations 1. **Owner verification**: All operations verify owner_id authorization 2. **Tool key authentication**: Supervisor operations require valid tool keys 3. **Data isolation**: Users cannot access other users' history 4. **Audit logging**: Log all rewind/fork operations for traceability ## Performance Considerations 1. **Pagination**: All list endpoints support cursor-based pagination 2. **Index usage**: Queries use appropriate indexes (contract_id, task_id, created_at) 3. **Snapshot cleanup**: Implement retention policy for old snapshots 4. **Response limits**: Cap response sizes for conversation history