summaryrefslogtreecommitdiff
path: root/makima/src
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src')
-rw-r--r--makima/src/bin/makima.rs44
-rw-r--r--makima/src/daemon/api/directive.rs199
-rw-r--r--makima/src/daemon/cli/directive.rs47
-rw-r--r--makima/src/daemon/cli/mod.rs18
-rw-r--r--makima/src/daemon/task/manager.rs144
-rw-r--r--makima/src/daemon/worktree/manager.rs79
-rw-r--r--makima/src/db/models.rs45
-rw-r--r--makima/src/db/repository.rs143
-rw-r--r--makima/src/orchestration/directive.rs84
-rw-r--r--makima/src/server/handlers/directives.rs394
-rw-r--r--makima/src/server/mod.rs4
-rw-r--r--makima/src/server/openapi.rs19
12 files changed, 194 insertions, 1026 deletions
diff --git a/makima/src/bin/makima.rs b/makima/src/bin/makima.rs
index d4af878..c2c9beb 100644
--- a/makima/src/bin/makima.rs
+++ b/makima/src/bin/makima.rs
@@ -825,50 +825,6 @@ async fn run_directive(
.await?;
println!("{}", serde_json::to_string(&result.0)?);
}
- DirectiveCommand::MemorySet(args) => {
- let client = ApiClient::new(args.common.api_url, args.common.api_key)?;
- let result = client
- .directive_memory_set(args.common.directive_id, &args.key, &args.value)
- .await?;
- println!("{}", serde_json::to_string(&result.0)?);
- }
- DirectiveCommand::MemoryGet(args) => {
- let client = ApiClient::new(args.common.api_url, args.common.api_key)?;
- let result = client
- .directive_memory_get(args.common.directive_id, &args.key)
- .await?;
- println!("{}", serde_json::to_string(&result.0)?);
- }
- DirectiveCommand::MemoryList(args) => {
- let client = ApiClient::new(args.api_url, args.api_key)?;
- let result = client
- .directive_memory_list(args.directive_id)
- .await?;
- println!("{}", serde_json::to_string(&result.0)?);
- }
- DirectiveCommand::MemoryDelete(args) => {
- let client = ApiClient::new(args.common.api_url, args.common.api_key)?;
- client
- .directive_memory_delete(args.common.directive_id, &args.key)
- .await?;
- println!(r#"{{"success": true}}"#);
- }
- DirectiveCommand::MemoryClear(args) => {
- let client = ApiClient::new(args.api_url, args.api_key)?;
- client
- .directive_memory_clear(args.directive_id)
- .await?;
- println!(r#"{{"success": true}}"#);
- }
- DirectiveCommand::MemoryBatchSet(args) => {
- let client = ApiClient::new(args.common.api_url, args.common.api_key)?;
- let entries: serde_json::Value = serde_json::from_str(&args.json)
- .map_err(|e| format!("Invalid JSON: {}", e))?;
- let result = client
- .directive_memory_batch_set(args.common.directive_id, entries)
- .await?;
- println!("{}", serde_json::to_string(&result.0)?);
- }
}
Ok(())
diff --git a/makima/src/daemon/api/directive.rs b/makima/src/daemon/api/directive.rs
index fcc2ca5..a0cdab0 100644
--- a/makima/src/daemon/api/directive.rs
+++ b/makima/src/daemon/api/directive.rs
@@ -30,54 +30,6 @@ pub struct UpdateStepDepsRequest {
pub depends_on: Vec<Uuid>,
}
-/// Percent-encode a string for use as a URL path segment.
-///
-/// Encodes all characters except unreserved characters (alphanumeric, `-`, `.`, `_`, `~`).
-fn percent_encode_path(s: &str) -> String {
- let mut encoded = String::with_capacity(s.len());
- for byte in s.bytes() {
- match byte {
- b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
- encoded.push(byte as char);
- }
- _ => {
- encoded.push_str(&format!("%{:02X}", byte));
- }
- }
- }
- encoded
-}
-
-/// Request body for setting a single memory entry.
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SetMemoryRequest {
- pub key: String,
- pub value: String,
-}
-
-/// A single entry within a batch set request.
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct BatchMemoryEntry {
- pub key: String,
- pub value: String,
-}
-
-/// Request body for setting multiple memory entries at once.
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct BatchSetMemoryRequest {
- pub entries: Vec<BatchMemoryEntry>,
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct MemorySetRequest {
- pub value: String,
-}
-
-
impl ApiClient {
/// List all directives.
pub async fn list_directives(&self) -> Result<JsonValue, ApiError> {
@@ -194,157 +146,6 @@ impl ApiClient {
self.put(&format!("/api/v1/directives/{}", directive_id), &req).await
}
- // ── Directive Memory ──────────────────────────────────────────────
-
- /// List all memory entries for a directive.
- pub async fn list_memories(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
- self.get(&format!("/api/v1/directives/{}/memory", directive_id))
- .await
- }
-
- /// Get a single memory entry by key.
- pub async fn get_memory(
- &self,
- directive_id: Uuid,
- key: &str,
- ) -> Result<JsonValue, ApiError> {
- self.get(&format!(
- "/api/v1/directives/{}/memory/{}",
- directive_id,
- percent_encode_path(key)
- ))
- .await
- }
-
- /// Set (create or update) a single memory entry.
- pub async fn set_memory(
- &self,
- directive_id: Uuid,
- key: &str,
- value: &str,
- ) -> Result<JsonValue, ApiError> {
- let req = SetMemoryRequest {
- key: key.to_string(),
- value: value.to_string(),
- };
- self.put(&format!("/api/v1/directives/{}/memory", directive_id), &req)
- .await
- }
-
- /// Set multiple memory entries in a single request.
- pub async fn batch_set_memories(
- &self,
- directive_id: Uuid,
- entries: Vec<(String, String)>,
- ) -> Result<JsonValue, ApiError> {
- let req = BatchSetMemoryRequest {
- entries: entries
- .into_iter()
- .map(|(key, value)| BatchMemoryEntry { key, value })
- .collect(),
- };
- self.post(
- &format!("/api/v1/directives/{}/memory/batch", directive_id),
- &req,
- )
- .await
- }
-
- /// Delete a single memory entry by key.
- pub async fn delete_memory(
- &self,
- directive_id: Uuid,
- key: &str,
- ) -> Result<(), ApiError> {
- self.delete(&format!(
- "/api/v1/directives/{}/memory/{}",
- directive_id,
- percent_encode_path(key)
- ))
- .await
- }
-
- /// Clear all memory entries for a directive.
- pub async fn clear_memories(&self, directive_id: Uuid) -> Result<(), ApiError> {
- self.delete(&format!("/api/v1/directives/{}/memory", directive_id))
- .await
- }
-
- // ── CLI-facing Directive Memory aliases ──────────────────────────
-
- /// Set a memory key-value pair for a directive (CLI-facing).
- pub async fn directive_memory_set(
- &self,
- directive_id: Uuid,
- key: &str,
- value: &str,
- ) -> Result<JsonValue, ApiError> {
- let req = MemorySetRequest {
- value: value.to_string(),
- };
- self.put(
- &format!("/api/v1/directives/{}/memory/{}", directive_id, key),
- &req,
- )
- .await
- }
-
- /// Get a memory value by key for a directive (CLI-facing).
- pub async fn directive_memory_get(
- &self,
- directive_id: Uuid,
- key: &str,
- ) -> Result<JsonValue, ApiError> {
- self.get(&format!(
- "/api/v1/directives/{}/memory/{}",
- directive_id, key
- ))
- .await
- }
-
- /// List all memory key-value pairs for a directive (CLI-facing).
- pub async fn directive_memory_list(
- &self,
- directive_id: Uuid,
- ) -> Result<JsonValue, ApiError> {
- self.get(&format!("/api/v1/directives/{}/memory", directive_id))
- .await
- }
-
- /// Delete a memory key for a directive (CLI-facing).
- pub async fn directive_memory_delete(
- &self,
- directive_id: Uuid,
- key: &str,
- ) -> Result<(), ApiError> {
- self.delete(&format!(
- "/api/v1/directives/{}/memory/{}",
- directive_id, key
- ))
- .await
- }
-
- /// Clear all memory for a directive (CLI-facing).
- pub async fn directive_memory_clear(
- &self,
- directive_id: Uuid,
- ) -> Result<(), ApiError> {
- self.delete(&format!("/api/v1/directives/{}/memory", directive_id))
- .await
- }
-
- /// Batch set multiple memory key-value pairs for a directive (CLI-facing).
- pub async fn directive_memory_batch_set(
- &self,
- directive_id: Uuid,
- entries: serde_json::Value,
- ) -> Result<JsonValue, ApiError> {
- self.post(
- &format!("/api/v1/directives/{}/memory/batch", directive_id),
- &entries,
- )
- .await
- }
}
#[derive(Serialize)]
diff --git a/makima/src/daemon/cli/directive.rs b/makima/src/daemon/cli/directive.rs
index 8eded77..7c50c42 100644
--- a/makima/src/daemon/cli/directive.rs
+++ b/makima/src/daemon/cli/directive.rs
@@ -126,50 +126,3 @@ pub struct UpdateArgs {
pub pr_branch: Option<String>,
}
-/// Arguments for memory-set command.
-#[derive(Args, Debug)]
-pub struct MemorySetArgs {
- #[command(flatten)]
- pub common: DirectiveArgs,
-
- /// Memory key
- pub key: String,
-
- /// Memory value
- pub value: String,
-}
-
-/// Arguments for memory-get command.
-#[derive(Args, Debug)]
-pub struct MemoryGetArgs {
- #[command(flatten)]
- pub common: DirectiveArgs,
-
- /// Memory key
- pub key: String,
-}
-
-/// Arguments for memory-list command (uses DirectiveArgs directly).
-
-/// Arguments for memory-delete command.
-#[derive(Args, Debug)]
-pub struct MemoryDeleteArgs {
- #[command(flatten)]
- pub common: DirectiveArgs,
-
- /// Memory key to delete
- pub key: String,
-}
-
-/// Arguments for memory-clear command (uses DirectiveArgs directly).
-
-/// Arguments for memory-batch-set command.
-#[derive(Args, Debug)]
-pub struct MemoryBatchSetArgs {
- #[command(flatten)]
- pub common: DirectiveArgs,
-
- /// JSON object of key-value pairs: {"key1":"value1","key2":"value2"}
- #[arg(long)]
- pub json: String,
-}
diff --git a/makima/src/daemon/cli/mod.rs b/makima/src/daemon/cli/mod.rs
index a78e5f8..bcaaa70 100644
--- a/makima/src/daemon/cli/mod.rs
+++ b/makima/src/daemon/cli/mod.rs
@@ -249,24 +249,6 @@ pub enum DirectiveCommand {
/// Update directive metadata (PR URL, etc.)
Update(directive::UpdateArgs),
-
- /// Set a memory key-value pair for the directive
- MemorySet(directive::MemorySetArgs),
-
- /// Get a memory value by key
- MemoryGet(directive::MemoryGetArgs),
-
- /// List all memory key-value pairs
- MemoryList(DirectiveArgs),
-
- /// Delete a memory key
- MemoryDelete(directive::MemoryDeleteArgs),
-
- /// Clear all memory for the directive
- MemoryClear(DirectiveArgs),
-
- /// Batch set multiple memory key-value pairs from JSON
- MemoryBatchSet(directive::MemoryBatchSetArgs),
}
impl Cli {
diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs
index 22b41d9..ce5a580 100644
--- a/makima/src/daemon/task/manager.rs
+++ b/makima/src/daemon/task/manager.rs
@@ -20,7 +20,7 @@ use crate::daemon::error::{DaemonError, TaskError, TaskResult};
use crate::daemon::process::{ClaudeInputMessage, ProcessManager};
use crate::daemon::storage;
use crate::daemon::temp::TempManager;
-use crate::daemon::worktree::{is_new_repo_request, ConflictResolution, WorktreeInfo, WorktreeManager};
+use crate::daemon::worktree::{is_new_repo_request, ConflictResolution, WorktreeError, WorktreeInfo, WorktreeManager};
use crate::daemon::db::local::LocalDb;
use crate::daemon::ws::{BranchInfo, DaemonCommand, DaemonMessage};
@@ -4406,8 +4406,14 @@ impl TaskManagerInner {
// Create worktree - either from scratch or copying from another task
let task_name = format!("task-{}", &task_id.to_string()[..8]);
let worktree_info = if let Some(from_task_id) = continue_from_task_id {
- // Try to find the source task's worktree path
- match self.find_worktree_for_task(from_task_id).await {
+ // Fallback chain for continuing from a previous task:
+ // 1. Try copying from existing worktree (fastest, preserves uncommitted changes)
+ // 2. Try creating from pushed branch (branch was pushed to remote)
+ // 3. Try restoring from saved patch data
+ // 4. Fail if none available
+
+ // Step 1: Try copying from existing worktree
+ let copy_result = match self.find_worktree_for_task(from_task_id).await {
Ok(source_worktree) => {
let msg = DaemonMessage::task_output(
task_id,
@@ -4416,66 +4422,112 @@ impl TaskManagerInner {
);
let _ = self.ws_tx.send(msg).await;
- // Create worktree by copying from source task
self.worktree_manager
.create_worktree_from_task(&source_worktree, task_id, &task_name)
.await
- .map_err(|e| DaemonError::Task(TaskError::SetupFailed(e.to_string())))?
}
- Err(worktree_err) => {
- // Source worktree not found - try to recover using patch data
- if let (Some(patch_str), Some(base_sha)) = (&patch_data, &patch_base_sha) {
- tracing::info!(
- task_id = %task_id,
- from_task_id = %from_task_id,
- base_sha = %base_sha,
- patch_len = patch_str.len(),
- "Source worktree not found, attempting to restore from patch"
- );
+ Err(e) => Err(crate::daemon::worktree::WorktreeError::RepoNotFound(e.to_string())),
+ };
- let msg = DaemonMessage::task_output(
- task_id,
- format!("Source worktree unavailable, restoring from checkpoint patch...\n"),
- false,
- );
- let _ = self.ws_tx.send(msg).await;
+ match copy_result {
+ Ok(info) => info,
+ Err(copy_err) => {
+ tracing::warn!(
+ task_id = %task_id,
+ from_task_id = %from_task_id,
+ error = %copy_err,
+ "Failed to copy from source worktree, trying branch fallback"
+ );
+
+ // Step 2: Try creating from pushed branch
+ let msg = DaemonMessage::task_output(
+ task_id,
+ format!("Source worktree unavailable, checking for pushed branch...\n"),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+
+ match self.worktree_manager
+ .create_worktree_from_task_branch(&source_repo, from_task_id, task_id, &task_name)
+ .await
+ {
+ Ok(info) => {
+ tracing::info!(
+ task_id = %task_id,
+ from_task_id = %from_task_id,
+ branch = %info.branch,
+ "Successfully created worktree from pushed branch"
+ );
+ let msg = DaemonMessage::task_output(
+ task_id,
+ format!("Restored from pushed branch {}\n", info.branch),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+ info
+ }
+ Err(branch_err) => {
+ tracing::warn!(
+ task_id = %task_id,
+ from_task_id = %from_task_id,
+ error = %branch_err,
+ "No pushed branch found, trying patch fallback"
+ );
+
+ // Step 3: Try restoring from saved patch data
+ if let (Some(patch_str), Some(base_sha)) = (&patch_data, &patch_base_sha) {
+ tracing::info!(
+ task_id = %task_id,
+ from_task_id = %from_task_id,
+ base_sha = %base_sha,
+ patch_len = patch_str.len(),
+ "Attempting to restore from checkpoint patch"
+ );
- // Decode base64 patch data
- match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, patch_str) {
- Ok(patch_bytes) => {
- match self.worktree_manager.restore_from_patch(
- source,
+ let msg = DaemonMessage::task_output(
task_id,
- &task_name,
- base_sha,
- &patch_bytes,
- ).await {
- Ok(worktree_info) => {
- tracing::info!(
- task_id = %task_id,
- path = %worktree_info.path.display(),
- "Successfully restored worktree from patch"
- );
- worktree_info
+ format!("Restoring from checkpoint patch...\n"),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+
+ match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, patch_str) {
+ Ok(patch_bytes) => {
+ match self.worktree_manager.restore_from_patch(
+ source,
+ task_id,
+ &task_name,
+ base_sha,
+ &patch_bytes,
+ ).await {
+ Ok(worktree_info) => {
+ tracing::info!(
+ task_id = %task_id,
+ path = %worktree_info.path.display(),
+ "Successfully restored worktree from patch"
+ );
+ worktree_info
+ }
+ Err(e) => {
+ return Err(DaemonError::Task(TaskError::SetupFailed(
+ format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), patch restore failed ({})", from_task_id, copy_err, branch_err, e)
+ )));
+ }
+ }
}
Err(e) => {
return Err(DaemonError::Task(TaskError::SetupFailed(
- format!("Cannot continue from task {}: {} (patch restore also failed: {})", from_task_id, worktree_err, e)
+ format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), patch decode failed ({})", from_task_id, copy_err, branch_err, e)
)));
}
}
- }
- Err(e) => {
+ } else {
+ // Step 4: No fallback available
return Err(DaemonError::Task(TaskError::SetupFailed(
- format!("Cannot continue from task {}: {} (patch decode failed: {})", from_task_id, worktree_err, e)
+ format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), no patch data available", from_task_id, copy_err, branch_err)
)));
}
}
- } else {
- // No patch data available - fail with original error
- return Err(DaemonError::Task(TaskError::SetupFailed(
- format!("Cannot continue from task {}: {}", from_task_id, worktree_err)
- )));
}
}
}
diff --git a/makima/src/daemon/worktree/manager.rs b/makima/src/daemon/worktree/manager.rs
index 20c93b1..04180b8 100644
--- a/makima/src/daemon/worktree/manager.rs
+++ b/makima/src/daemon/worktree/manager.rs
@@ -692,6 +692,85 @@ impl WorktreeManager {
})
}
+ /// Find and create a worktree from a previously pushed branch for a task.
+ ///
+ /// Searches local and remote branches for ones matching the task's UUID,
+ /// then creates a new worktree based on that branch.
+ pub async fn create_worktree_from_task_branch(
+ &self,
+ source_repo: &Path,
+ from_task_id: Uuid,
+ new_task_id: Uuid,
+ new_task_name: &str,
+ ) -> Result<WorktreeInfo, WorktreeError> {
+ let from_short_id = short_uuid(from_task_id);
+ let from_full_id = from_task_id.to_string();
+
+ tracing::info!(
+ from_task_id = %from_task_id,
+ new_task_id = %new_task_id,
+ "Searching for pushed branch to continue from"
+ );
+
+ // Fetch latest from remote first
+ let _ = Command::new("git")
+ .args(["fetch", "--all", "--prune"])
+ .current_dir(source_repo)
+ .output()
+ .await;
+
+ // Search for branches matching the task ID (both local and remote)
+ let output = Command::new("git")
+ .args(["branch", "-a", "--format=%(refname:short)"])
+ .current_dir(source_repo)
+ .output()
+ .await?;
+
+ if !output.status.success() {
+ return Err(WorktreeError::GitCommand(
+ "Failed to list branches".to_string(),
+ ));
+ }
+
+ let branches_output = String::from_utf8_lossy(&output.stdout);
+ let mut found_branch: Option<String> = None;
+
+ for line in branches_output.lines() {
+ let b = line.trim();
+ if b.is_empty() {
+ continue;
+ }
+ // Match branches containing the full UUID or short UUID
+ if b.contains(&from_full_id) || b.contains(&from_short_id) {
+ // Prefer local branches, but accept remote
+ if let Some(remote_branch) = b.strip_prefix("origin/") {
+ if found_branch.is_none() {
+ found_branch = Some(remote_branch.to_string());
+ }
+ } else {
+ found_branch = Some(b.to_string());
+ }
+ }
+ }
+
+ let from_branch = found_branch.ok_or_else(|| {
+ WorktreeError::GitCommand(format!(
+ "No branch found for task {} in repository",
+ from_short_id
+ ))
+ })?;
+
+ tracing::info!(
+ from_task_id = %from_task_id,
+ from_branch = %from_branch,
+ "Found pushed branch, creating worktree from it"
+ );
+
+ // Create a worktree using the found branch as the base
+ self.create_worktree(source_repo, new_task_id, new_task_name, &from_branch)
+ .await
+ }
+
/// Remove a worktree and optionally its branch.
pub async fn remove_worktree(
&self,
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs
index 66c0a30..131dffc 100644
--- a/makima/src/db/models.rs
+++ b/makima/src/db/models.rs
@@ -2714,7 +2714,6 @@ pub struct Directive {
pub pr_url: Option<String>,
pub pr_branch: Option<String>,
pub completion_task_id: Option<Uuid>,
- pub memory_enabled: bool,
pub goal_updated_at: DateTime<Utc>,
pub started_at: Option<DateTime<Utc>>,
pub version: i32,
@@ -2764,7 +2763,6 @@ pub struct DirectiveSummary {
pub orchestrator_task_id: Option<Uuid>,
pub pr_url: Option<String>,
pub completion_task_id: Option<Uuid>,
- pub memory_enabled: bool,
pub version: i32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
@@ -2791,8 +2789,6 @@ pub struct CreateDirectiveRequest {
pub repository_url: Option<String>,
pub local_path: Option<String>,
pub base_branch: Option<String>,
- #[serde(default)]
- pub memory_enabled: bool,
}
/// Request to update a directive.
@@ -2808,7 +2804,6 @@ pub struct UpdateDirectiveRequest {
pub orchestrator_task_id: Option<Uuid>,
pub pr_url: Option<String>,
pub pr_branch: Option<String>,
- pub memory_enabled: Option<bool>,
pub version: Option<i32>,
}
@@ -2853,43 +2848,3 @@ pub struct UpdateDirectiveStepRequest {
pub order_index: Option<i32>,
}
-// =============================================================================
-// Directive Memory Types
-// =============================================================================
-
-/// A memory entry for a directive — key-value context that persists across tasks.
-#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct DirectiveMemory {
- pub id: Uuid,
- pub directive_id: Uuid,
- pub key: String,
- pub value: String,
- pub category: Option<String>,
- pub created_at: DateTime<Utc>,
- pub updated_at: DateTime<Utc>,
-}
-
-/// Request to set a memory entry (upsert by key).
-#[derive(Debug, Deserialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct SetDirectiveMemoryRequest {
- pub key: String,
- pub value: String,
- pub category: Option<String>,
-}
-
-/// Request to batch set memory entries.
-#[derive(Debug, Deserialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct BatchSetDirectiveMemoryRequest {
- pub entries: Vec<SetDirectiveMemoryRequest>,
-}
-
-/// Response for listing memories.
-#[derive(Debug, Serialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct DirectiveMemoryListResponse {
- pub memories: Vec<DirectiveMemory>,
- pub total: i64,
-}
diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs
index 51f49cd..8923f97 100644
--- a/makima/src/db/repository.rs
+++ b/makima/src/db/repository.rs
@@ -11,10 +11,9 @@ use super::models::{
ContractTypeTemplateRecord, ConversationMessage, ConversationSnapshot,
CreateContractRequest, CreateFileRequest, CreateTaskRequest,
CreateTemplateRequest, Daemon, DaemonTaskAssignment, DaemonWithCapacity,
- DeliverableDefinition, Directive, DirectiveMemory, DirectiveStep, DirectiveSummary,
+ DeliverableDefinition, Directive, DirectiveStep, DirectiveSummary,
CreateDirectiveRequest, CreateDirectiveStepRequest, UpdateDirectiveRequest,
- UpdateDirectiveStepRequest, SetDirectiveMemoryRequest,
- BatchSetDirectiveMemoryRequest, DirectiveMemoryListResponse,
+ UpdateDirectiveStepRequest,
File, FileSummary, FileVersion, HistoryEvent, HistoryQueryFilters,
MeshChatConversation, MeshChatMessageRecord, PhaseChangeResult, PhaseConfig,
PhaseDefinition, SupervisorHeartbeatRecord, SupervisorState,
@@ -4930,8 +4929,8 @@ pub async fn create_directive_for_owner(
) -> Result<Directive, sqlx::Error> {
sqlx::query_as::<_, Directive>(
r#"
- INSERT INTO directives (owner_id, title, goal, repository_url, local_path, base_branch, memory_enabled)
- VALUES ($1, $2, $3, $4, $5, $6, $7)
+ INSERT INTO directives (owner_id, title, goal, repository_url, local_path, base_branch)
+ VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
"#,
)
@@ -4941,7 +4940,6 @@ pub async fn create_directive_for_owner(
.bind(&req.repository_url)
.bind(&req.local_path)
.bind(&req.base_branch)
- .bind(req.memory_enabled)
.fetch_one(pool)
.await
}
@@ -4994,7 +4992,7 @@ pub async fn list_directives_for_owner(
SELECT
d.id, d.owner_id, d.title, d.goal, d.status, d.repository_url,
d.orchestrator_task_id, d.pr_url, d.completion_task_id,
- d.memory_enabled, d.version, d.created_at, d.updated_at,
+ d.version, d.created_at, d.updated_at,
COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id), 0) as total_steps,
COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id AND status = 'completed'), 0) as completed_steps,
COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id AND status = 'running'), 0) as running_steps,
@@ -5048,13 +5046,12 @@ pub async fn update_directive_for_owner(
let orchestrator_task_id = req.orchestrator_task_id.or(current.orchestrator_task_id);
let pr_url = req.pr_url.as_deref().or(current.pr_url.as_deref());
let pr_branch = req.pr_branch.as_deref().or(current.pr_branch.as_deref());
- let memory_enabled = req.memory_enabled.unwrap_or(current.memory_enabled);
let result = sqlx::query_as::<_, Directive>(
r#"
UPDATE directives
SET title = $3, goal = $4, status = $5, repository_url = $6, local_path = $7,
- base_branch = $8, orchestrator_task_id = $9, pr_url = $10, pr_branch = $11, memory_enabled = $12,
+ base_branch = $8, orchestrator_task_id = $9, pr_url = $10, pr_branch = $11,
version = version + 1, updated_at = NOW()
WHERE id = $1 AND owner_id = $2
RETURNING *
@@ -5071,7 +5068,6 @@ pub async fn update_directive_for_owner(
.bind(orchestrator_task_id)
.bind(pr_url)
.bind(pr_branch)
- .bind(memory_enabled)
.fetch_optional(pool)
.await
.map_err(RepositoryError::Database)?;
@@ -5609,7 +5605,6 @@ pub struct StepForDispatch {
pub directive_title: String,
pub repository_url: Option<String>,
pub base_branch: Option<String>,
- pub memory_enabled: bool,
/// The directive's PR branch (if a PR has already been created from previous steps).
pub pr_branch: Option<String>,
}
@@ -5633,7 +5628,6 @@ pub async fn get_ready_steps_for_dispatch(
d.title AS directive_title,
d.repository_url,
d.base_branch,
- d.memory_enabled,
d.pr_branch
FROM directive_steps ds
JOIN directives d ON d.id = ds.directive_id
@@ -5895,128 +5889,3 @@ pub async fn get_directive_max_generation(
Ok(row.0.unwrap_or(0))
}
-// =============================================================================
-// Directive Memory CRUD
-// =============================================================================
-
-/// List all memories for a directive, optionally filtered by category.
-pub async fn list_directive_memories(
- pool: &PgPool,
- directive_id: Uuid,
- category: Option<&str>,
-) -> Result<Vec<DirectiveMemory>, sqlx::Error> {
- match category {
- Some(cat) => {
- sqlx::query_as::<_, DirectiveMemory>(
- r#"
- SELECT * FROM directive_memories
- WHERE directive_id = $1 AND category = $2
- ORDER BY key
- "#,
- )
- .bind(directive_id)
- .bind(cat)
- .fetch_all(pool)
- .await
- }
- None => {
- sqlx::query_as::<_, DirectiveMemory>(
- r#"
- SELECT * FROM directive_memories
- WHERE directive_id = $1
- ORDER BY key
- "#,
- )
- .bind(directive_id)
- .fetch_all(pool)
- .await
- }
- }
-}
-
-/// Get a single memory entry by directive ID and key.
-pub async fn get_directive_memory(
- pool: &PgPool,
- directive_id: Uuid,
- key: &str,
-) -> Result<Option<DirectiveMemory>, sqlx::Error> {
- sqlx::query_as::<_, DirectiveMemory>(
- r#"
- SELECT * FROM directive_memories
- WHERE directive_id = $1 AND key = $2
- "#,
- )
- .bind(directive_id)
- .bind(key)
- .fetch_optional(pool)
- .await
-}
-
-/// Set (upsert) a memory entry for a directive.
-pub async fn set_directive_memory(
- pool: &PgPool,
- directive_id: Uuid,
- req: &SetDirectiveMemoryRequest,
-) -> Result<DirectiveMemory, sqlx::Error> {
- sqlx::query_as::<_, DirectiveMemory>(
- r#"
- INSERT INTO directive_memories (directive_id, key, value, category)
- VALUES ($1, $2, $3, $4)
- ON CONFLICT (directive_id, key)
- DO UPDATE SET value = EXCLUDED.value,
- category = EXCLUDED.category,
- updated_at = NOW()
- RETURNING *
- "#,
- )
- .bind(directive_id)
- .bind(&req.key)
- .bind(&req.value)
- .bind(&req.category)
- .fetch_one(pool)
- .await
-}
-
-/// Batch set memory entries for a directive.
-pub async fn batch_set_directive_memories(
- pool: &PgPool,
- directive_id: Uuid,
- memories: &[SetDirectiveMemoryRequest],
-) -> Result<Vec<DirectiveMemory>, sqlx::Error> {
- let mut results = Vec::with_capacity(memories.len());
- for mem in memories {
- let result = set_directive_memory(pool, directive_id, mem).await?;
- results.push(result);
- }
- Ok(results)
-}
-
-/// Delete a single memory entry by key.
-pub async fn delete_directive_memory(
- pool: &PgPool,
- directive_id: Uuid,
- key: &str,
-) -> Result<bool, sqlx::Error> {
- let result = sqlx::query(
- r#"DELETE FROM directive_memories WHERE directive_id = $1 AND key = $2"#,
- )
- .bind(directive_id)
- .bind(key)
- .execute(pool)
- .await?;
- Ok(result.rows_affected() > 0)
-}
-
-/// Delete all memory entries for a directive.
-pub async fn clear_directive_memories(
- pool: &PgPool,
- directive_id: Uuid,
-) -> Result<u64, sqlx::Error> {
- let result = sqlx::query(
- r#"DELETE FROM directive_memories WHERE directive_id = $1"#,
- )
- .bind(directive_id)
- .execute(pool)
- .await?;
- Ok(result.rows_affected())
-}
diff --git a/makima/src/orchestration/directive.rs b/makima/src/orchestration/directive.rs
index 344bdf5..bb573d4 100644
--- a/makima/src/orchestration/directive.rs
+++ b/makima/src/orchestration/directive.rs
@@ -9,7 +9,7 @@
use sqlx::PgPool;
use uuid::Uuid;
-use crate::db::models::{CreateTaskRequest, DirectiveMemory, UpdateTaskRequest};
+use crate::db::models::{CreateTaskRequest, UpdateTaskRequest};
use crate::db::repository;
use crate::server::state::{DaemonCommand, SharedState};
@@ -44,24 +44,7 @@ impl DirectiveOrchestrator {
"Directive needs planning — spawning planning task"
);
- // Load memories if memory is enabled for this directive
- let memories = if directive.memory_enabled {
- match repository::list_directive_memories(&self.pool, directive.id, None).await {
- Ok(m) => m,
- Err(e) => {
- tracing::warn!(
- directive_id = %directive.id,
- error = %e,
- "Failed to load directive memories for planning — continuing without"
- );
- vec![]
- }
- }
- } else {
- vec![]
- };
-
- let plan = build_planning_prompt(&directive, &[], 1, &memories);
+ let plan = build_planning_prompt(&directive, &[], 1);
if let Err(e) = self
.spawn_orchestrator_task(
@@ -143,27 +126,6 @@ impl DirectiveOrchestrator {
.as_deref()
.unwrap_or("Execute the step described below.");
- // Load memories if memory is enabled for this directive
- let memory_context = if step.memory_enabled {
- match repository::list_directive_memories(&self.pool, step.directive_id, None).await {
- Ok(memories) if !memories.is_empty() => {
- format!("\n\nMEMORY CONTEXT (from previous planning/execution cycles):\n{}\n",
- format_memories_for_prompt(&memories))
- }
- Ok(_) => String::new(),
- Err(e) => {
- tracing::warn!(
- directive_id = %step.directive_id,
- error = %e,
- "Failed to load directive memories for execution — continuing without"
- );
- String::new()
- }
- }
- } else {
- String::new()
- };
-
// Build merge instructions for additional dependencies (beyond the first)
let merge_preamble = if dep_tasks.len() > 1 {
use crate::daemon::worktree::{sanitize_name, short_uuid};
@@ -196,7 +158,6 @@ impl DirectiveOrchestrator {
DESCRIPTION: {description}\n\n\
{merge_preamble}\
INSTRUCTIONS:\n{task_plan}\n\
- {memory_context}\
When done, the system will automatically mark this step as completed.\n\
If you cannot complete the task, report the failure clearly.",
directive_title = step.directive_title,
@@ -204,7 +165,6 @@ impl DirectiveOrchestrator {
description = step.step_description.as_deref().unwrap_or("(none)"),
merge_preamble = merge_preamble,
task_plan = task_plan,
- memory_context = memory_context,
);
match self
@@ -348,24 +308,7 @@ impl DirectiveOrchestrator {
let generation =
repository::get_directive_max_generation(&self.pool, directive.id).await? + 1;
- // Load memories if memory is enabled for this directive
- let memories = if directive.memory_enabled {
- match repository::list_directive_memories(&self.pool, directive.id, None).await {
- Ok(m) => m,
- Err(e) => {
- tracing::warn!(
- directive_id = %directive.id,
- error = %e,
- "Failed to load directive memories for re-planning — continuing without"
- );
- vec![]
- }
- }
- } else {
- vec![]
- };
-
- let plan = build_planning_prompt(&directive, &existing_steps, generation, &memories);
+ let plan = build_planning_prompt(&directive, &existing_steps, generation);
if let Err(e) = self
.spawn_orchestrator_task(
@@ -747,35 +690,14 @@ impl DirectiveOrchestrator {
}
}
-/// Format memory entries into a readable prompt section.
-fn format_memories_for_prompt(memories: &[DirectiveMemory]) -> String {
- let mut out = String::new();
- for memory in memories {
- let cat = memory.category.as_deref().unwrap_or("other");
- out.push_str(&format!(
- "- [{}] {}: {}\n",
- cat, memory.key, memory.value
- ));
- }
- out
-}
-
/// Build the planning prompt for a directive.
fn build_planning_prompt(
directive: &crate::db::models::Directive,
existing_steps: &[crate::db::models::DirectiveStep],
generation: i32,
- memories: &[DirectiveMemory],
) -> String {
let mut prompt = String::new();
- // Include memory context if available
- if !memories.is_empty() {
- prompt.push_str("MEMORY CONTEXT (insights and decisions from previous cycles):\n");
- prompt.push_str(&format_memories_for_prompt(memories));
- prompt.push_str("\nUse these memories to inform your planning. Avoid repeating past mistakes and build on prior insights.\n\n");
- }
-
if !existing_steps.is_empty() {
prompt.push_str(&format!(
"EXISTING STEPS (generation {}):\n",
diff --git a/makima/src/server/handlers/directives.rs b/makima/src/server/handlers/directives.rs
index 9314031..25b2dc4 100644
--- a/makima/src/server/handlers/directives.rs
+++ b/makima/src/server/handlers/directives.rs
@@ -1,18 +1,17 @@
//! HTTP handlers for directive CRUD and DAG progression.
use axum::{
- extract::{Path, Query, State},
+ extract::{Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
-use serde::Deserialize;
use uuid::Uuid;
use crate::db::models::{
- BatchSetDirectiveMemoryRequest, CleanupTasksResponse, CreateDirectiveRequest,
- CreateDirectiveStepRequest, Directive, DirectiveListResponse, DirectiveMemory,
- DirectiveMemoryListResponse, DirectiveStep, DirectiveWithSteps, SetDirectiveMemoryRequest,
+ CleanupTasksResponse, CreateDirectiveRequest,
+ CreateDirectiveStepRequest, Directive, DirectiveListResponse,
+ DirectiveStep, DirectiveWithSteps,
UpdateDirectiveRequest, UpdateDirectiveStepRequest, UpdateGoalRequest,
};
use crate::db::repository;
@@ -20,12 +19,6 @@ use crate::server::auth::Authenticated;
use crate::server::messages::ApiError;
use crate::server::state::SharedState;
-/// Query parameters for the memory list endpoint.
-#[derive(Debug, Deserialize)]
-pub struct MemoryListQuery {
- pub category: Option<String>,
-}
-
// =============================================================================
// Directive CRUD
// =============================================================================
@@ -849,385 +842,6 @@ pub async fn update_goal(
}
// =============================================================================
-// Directive Memory CRUD
-// =============================================================================
-
-/// List all memories for a directive, optionally filtered by category.
-#[utoipa::path(
- get,
- path = "/api/v1/directives/{id}/memories",
- params(
- ("id" = Uuid, Path, description = "Directive ID"),
- ("category" = Option<String>, Query, description = "Filter by category"),
- ),
- responses(
- (status = 200, description = "List of memories", body = DirectiveMemoryListResponse),
- (status = 404, description = "Directive not found", body = ApiError),
- (status = 503, description = "Database not configured", body = ApiError),
- ),
- security(("bearer_auth" = []), ("api_key" = [])),
- tag = "Directives"
-)]
-pub async fn list_memories(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path(id): Path<Uuid>,
- Query(query): Query<MemoryListQuery>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- .into_response();
- };
-
- // Verify directive ownership
- match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
- Ok(Some(_)) => {}
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Directive not found")),
- )
- .into_response();
- }
- Err(e) => {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("GET_FAILED", &e.to_string())),
- )
- .into_response();
- }
- }
-
- match repository::list_directive_memories(pool, id, query.category.as_deref()).await {
- Ok(memories) => {
- let total = memories.len() as i64;
- Json(DirectiveMemoryListResponse { memories, total }).into_response()
- }
- Err(e) => {
- tracing::error!("Failed to list memories: {}", e);
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("LIST_FAILED", &e.to_string())),
- )
- .into_response()
- }
- }
-}
-
-/// Get a single memory entry by key.
-#[utoipa::path(
- get,
- path = "/api/v1/directives/{id}/memories/{key}",
- params(
- ("id" = Uuid, Path, description = "Directive ID"),
- ("key" = String, Path, description = "Memory key"),
- ),
- responses(
- (status = 200, description = "Memory entry", body = DirectiveMemory),
- (status = 404, description = "Not found", body = ApiError),
- (status = 503, description = "Database not configured", body = ApiError),
- ),
- security(("bearer_auth" = []), ("api_key" = [])),
- tag = "Directives"
-)]
-pub async fn get_memory(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path((id, key)): Path<(Uuid, String)>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- .into_response();
- };
-
- // Verify directive ownership
- match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
- Ok(Some(_)) => {}
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Directive not found")),
- )
- .into_response();
- }
- Err(e) => {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("GET_FAILED", &e.to_string())),
- )
- .into_response();
- }
- }
-
- match repository::get_directive_memory(pool, id, &key).await {
- Ok(Some(memory)) => Json(memory).into_response(),
- Ok(None) => (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Memory entry not found")),
- )
- .into_response(),
- Err(e) => {
- tracing::error!("Failed to get memory: {}", e);
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("GET_FAILED", &e.to_string())),
- )
- .into_response()
- }
- }
-}
-
-/// Set (upsert) a single memory entry.
-#[utoipa::path(
- post,
- path = "/api/v1/directives/{id}/memories",
- params(("id" = Uuid, Path, description = "Directive ID")),
- request_body = SetDirectiveMemoryRequest,
- responses(
- (status = 200, description = "Memory entry set", body = DirectiveMemory),
- (status = 404, description = "Directive not found", body = ApiError),
- (status = 503, description = "Database not configured", body = ApiError),
- ),
- security(("bearer_auth" = []), ("api_key" = [])),
- tag = "Directives"
-)]
-pub async fn set_memory(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path(id): Path<Uuid>,
- Json(req): Json<SetDirectiveMemoryRequest>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- .into_response();
- };
-
- // Verify directive ownership
- match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
- Ok(Some(_)) => {}
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Directive not found")),
- )
- .into_response();
- }
- Err(e) => {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("GET_FAILED", &e.to_string())),
- )
- .into_response();
- }
- }
-
- match repository::set_directive_memory(pool, id, &req).await {
- Ok(memory) => Json(memory).into_response(),
- Err(e) => {
- tracing::error!("Failed to set memory: {}", e);
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("SET_FAILED", &e.to_string())),
- )
- .into_response()
- }
- }
-}
-
-/// Batch set multiple memory entries.
-#[utoipa::path(
- post,
- path = "/api/v1/directives/{id}/memories/batch",
- params(("id" = Uuid, Path, description = "Directive ID")),
- request_body = BatchSetDirectiveMemoryRequest,
- responses(
- (status = 200, description = "Memory entries set", body = Vec<DirectiveMemory>),
- (status = 404, description = "Directive not found", body = ApiError),
- (status = 503, description = "Database not configured", body = ApiError),
- ),
- security(("bearer_auth" = []), ("api_key" = [])),
- tag = "Directives"
-)]
-pub async fn batch_set_memories(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path(id): Path<Uuid>,
- Json(req): Json<BatchSetDirectiveMemoryRequest>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- .into_response();
- };
-
- // Verify directive ownership
- match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
- Ok(Some(_)) => {}
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Directive not found")),
- )
- .into_response();
- }
- Err(e) => {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("GET_FAILED", &e.to_string())),
- )
- .into_response();
- }
- }
-
- match repository::batch_set_directive_memories(pool, id, &req.entries).await {
- Ok(memories) => Json(memories).into_response(),
- Err(e) => {
- tracing::error!("Failed to batch set memories: {}", e);
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("SET_FAILED", &e.to_string())),
- )
- .into_response()
- }
- }
-}
-
-/// Delete a single memory entry by key.
-#[utoipa::path(
- delete,
- path = "/api/v1/directives/{id}/memories/{key}",
- params(
- ("id" = Uuid, Path, description = "Directive ID"),
- ("key" = String, Path, description = "Memory key"),
- ),
- responses(
- (status = 204, description = "Deleted"),
- (status = 404, description = "Not found", body = ApiError),
- (status = 503, description = "Database not configured", body = ApiError),
- ),
- security(("bearer_auth" = []), ("api_key" = [])),
- tag = "Directives"
-)]
-pub async fn delete_memory(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path((id, key)): Path<(Uuid, String)>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- .into_response();
- };
-
- // Verify directive ownership
- match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
- Ok(Some(_)) => {}
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Directive not found")),
- )
- .into_response();
- }
- Err(e) => {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("GET_FAILED", &e.to_string())),
- )
- .into_response();
- }
- }
-
- match repository::delete_directive_memory(pool, id, &key).await {
- Ok(true) => StatusCode::NO_CONTENT.into_response(),
- Ok(false) => (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Memory entry not found")),
- )
- .into_response(),
- Err(e) => {
- tracing::error!("Failed to delete memory: {}", e);
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("DELETE_FAILED", &e.to_string())),
- )
- .into_response()
- }
- }
-}
-
-/// Clear all memories for a directive.
-#[utoipa::path(
- delete,
- path = "/api/v1/directives/{id}/memories",
- params(("id" = Uuid, Path, description = "Directive ID")),
- responses(
- (status = 204, description = "All memories cleared"),
- (status = 404, description = "Directive not found", body = ApiError),
- (status = 503, description = "Database not configured", body = ApiError),
- ),
- security(("bearer_auth" = []), ("api_key" = [])),
- tag = "Directives"
-)]
-pub async fn clear_memories(
- State(state): State<SharedState>,
- Authenticated(auth): Authenticated,
- Path(id): Path<Uuid>,
-) -> impl IntoResponse {
- let Some(ref pool) = state.db_pool else {
- return (
- StatusCode::SERVICE_UNAVAILABLE,
- Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")),
- )
- .into_response();
- };
-
- // Verify directive ownership
- match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
- Ok(Some(_)) => {}
- Ok(None) => {
- return (
- StatusCode::NOT_FOUND,
- Json(ApiError::new("NOT_FOUND", "Directive not found")),
- )
- .into_response();
- }
- Err(e) => {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("GET_FAILED", &e.to_string())),
- )
- .into_response();
- }
- }
-
- match repository::clear_directive_memories(pool, id).await {
- Ok(_) => StatusCode::NO_CONTENT.into_response(),
- Err(e) => {
- tracing::error!("Failed to clear memories: {}", e);
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- Json(ApiError::new("CLEAR_FAILED", &e.to_string())),
- )
- .into_response()
- }
- }
-}
-
-// =============================================================================
// Task Cleanup
// =============================================================================
diff --git a/makima/src/server/mod.rs b/makima/src/server/mod.rs
index 7110ef8..c1e1309 100644
--- a/makima/src/server/mod.rs
+++ b/makima/src/server/mod.rs
@@ -238,10 +238,6 @@ pub fn make_router(state: SharedState) -> Router {
.route("/directives/{id}/steps/{step_id}/skip", post(directives::skip_step))
.route("/directives/{id}/goal", put(directives::update_goal))
.route("/directives/{id}/cleanup-tasks", post(directives::cleanup_tasks))
- // Directive memory endpoints
- .route("/directives/{id}/memories", get(directives::list_memories).post(directives::set_memory).delete(directives::clear_memories))
- .route("/directives/{id}/memories/batch", post(directives::batch_set_memories))
- .route("/directives/{id}/memories/{key}", get(directives::get_memory).delete(directives::delete_memory))
// Timeline endpoint (unified history for user)
.route("/timeline", get(history::get_timeline))
// Contract type templates (built-in only)
diff --git a/makima/src/server/openapi.rs b/makima/src/server/openapi.rs
index f28b105..e68286e 100644
--- a/makima/src/server/openapi.rs
+++ b/makima/src/server/openapi.rs
@@ -3,7 +3,7 @@
use utoipa::OpenApi;
use crate::db::models::{
- AddLocalRepositoryRequest, AddRemoteRepositoryRequest, BatchSetDirectiveMemoryRequest,
+ AddLocalRepositoryRequest, AddRemoteRepositoryRequest,
BranchInfo, BranchListResponse, BranchTaskRequest, BranchTaskResponse,
ChangePhaseRequest,
Contract, ContractChatHistoryResponse, ContractChatMessageRecord, ContractEvent,
@@ -11,14 +11,14 @@ use crate::db::models::{
CleanupTasksResponse,
CreateContractRequest, CreateDirectiveRequest, CreateDirectiveStepRequest, CreateFileRequest,
CreateManagedRepositoryRequest, CreateTaskRequest, Daemon, DaemonDirectoriesResponse,
- DaemonDirectory, DaemonListResponse, Directive, DirectiveListResponse, DirectiveMemory,
- DirectiveMemoryListResponse, DirectiveStep, DirectiveSummary, DirectiveWithSteps,
+ DaemonDirectory, DaemonListResponse, Directive, DirectiveListResponse,
+ DirectiveStep, DirectiveSummary, DirectiveWithSteps,
File, FileListResponse, FileSummary,
MergeCommitRequest, MergeCompleteCheckResponse, MergeResolveRequest, MergeResultResponse,
MergeSkipRequest, MergeStartRequest, MergeStatusResponse, MeshChatConversation,
MeshChatHistoryResponse, MeshChatMessageRecord, RepositoryHistoryEntry,
RepositoryHistoryListResponse, RepositorySuggestionsQuery, SendMessageRequest,
- SetDirectiveMemoryRequest, Task,
+ Task,
TaskEventListResponse, TaskListResponse, TaskSummary, TaskWithSubtasks, TranscriptEntry,
UpdateContractRequest, UpdateDirectiveRequest, UpdateDirectiveStepRequest,
UpdateFileRequest, UpdateGoalRequest, UpdateTaskRequest,
@@ -125,13 +125,6 @@ use crate::server::messages::{ApiError, AudioEncoding, StartMessage, StopMessage
directives::skip_step,
directives::update_goal,
directives::cleanup_tasks,
- // Directive memory endpoints
- directives::list_memories,
- directives::get_memory,
- directives::set_memory,
- directives::batch_set_memories,
- directives::delete_memory,
- directives::clear_memories,
// Repository history/settings endpoints
repository_history::list_repository_history,
repository_history::get_repository_suggestions,
@@ -229,10 +222,6 @@ use crate::server::messages::{ApiError, AudioEncoding, StartMessage, StopMessage
CreateDirectiveStepRequest,
UpdateDirectiveStepRequest,
CleanupTasksResponse,
- DirectiveMemory,
- DirectiveMemoryListResponse,
- SetDirectiveMemoryRequest,
- BatchSetDirectiveMemoryRequest,
// Repository history schemas
RepositoryHistoryEntry,
RepositoryHistoryListResponse,