summaryrefslogtreecommitdiff
path: root/makima/src/daemon/api/directive.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-05 23:42:48 +0000
committersoryu <soryu@soryu.co>2026-02-05 23:42:48 +0000
commit88a4f15ce1310f8ee8693835be14aa5280233f17 (patch)
tree5c1a0417e02071d2198d13478ffa85533b19f891 /makima/src/daemon/api/directive.rs
parentf1a50b80f3969d150bd1c31edde0aff05369157e (diff)
downloadsoryu-88a4f15ce1310f8ee8693835be14aa5280233f17.tar.gz
soryu-88a4f15ce1310f8ee8693835be14aa5280233f17.zip
Add directive-first chain system redesign
Redesigns the chain system with a directive-first architecture where Directive is the top-level entity (the "why/what") and Chains are generated execution plans (the "how") that can be dynamically modified. Backend: - Add database migration for directive system tables - Add Directive, DirectiveChain, ChainStep, DirectiveEvent models - Add DirectiveVerifier and DirectiveApproval models - Add orchestration module with engine, planner, and verifier - Add comprehensive API handlers for directives - Add daemon CLI commands for directive management - Add directive skill documentation - Integrate contract completion with directive engine - Add SSE endpoint for real-time directive events Frontend: - Add directives route with split-view layout - Add 6-tab detail view (Overview, Chain, Events, Evaluations, Approvals, Verifiers) - Add React Flow DAG visualization for chain steps - Add SSE subscription hook for real-time event updates - Add useDirectives and useDirectiveEventSubscription hooks - Add directive types and API functions Fixes: - Fix test failures in ws/protocol, task_output, completion_gate, patch - Fix word boundary matching in looks_like_task() - Fix parse_last() to find actual last completion gate - Fix create_export_patch when merge-base equals HEAD - Clean up clippy warnings in new code Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/src/daemon/api/directive.rs')
-rw-r--r--makima/src/daemon/api/directive.rs162
1 files changed, 162 insertions, 0 deletions
diff --git a/makima/src/daemon/api/directive.rs b/makima/src/daemon/api/directive.rs
new file mode 100644
index 0000000..5281d21
--- /dev/null
+++ b/makima/src/daemon/api/directive.rs
@@ -0,0 +1,162 @@
+//! Directive API methods.
+
+use uuid::Uuid;
+
+use super::client::{ApiClient, ApiError};
+use super::supervisor::JsonValue;
+
+impl ApiClient {
+ /// Create a new directive.
+ pub async fn create_directive(
+ &self,
+ goal: &str,
+ repository_url: Option<&str>,
+ autonomy_level: &str,
+ ) -> Result<JsonValue, ApiError> {
+ #[derive(serde::Serialize)]
+ #[serde(rename_all = "camelCase")]
+ struct CreateRequest<'a> {
+ goal: &'a str,
+ repository_url: Option<&'a str>,
+ autonomy_level: &'a str,
+ }
+ let req = CreateRequest {
+ goal,
+ repository_url,
+ autonomy_level,
+ };
+ self.post("/api/v1/directives", &req).await
+ }
+
+ /// List all directives for the authenticated user.
+ pub async fn list_directives(
+ &self,
+ status: Option<&str>,
+ limit: i32,
+ ) -> Result<JsonValue, ApiError> {
+ let mut params = Vec::new();
+ if let Some(s) = status {
+ params.push(format!("status={}", s));
+ }
+ params.push(format!("limit={}", limit));
+ let query_string = format!("?{}", params.join("&"));
+ self.get(&format!("/api/v1/directives{}", query_string))
+ .await
+ }
+
+ /// Get a directive by ID (includes progress info).
+ pub async fn get_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.get(&format!("/api/v1/directives/{}", directive_id))
+ .await
+ }
+
+ /// Archive a directive.
+ pub async fn archive_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.delete_with_response(&format!("/api/v1/directives/{}", directive_id))
+ .await
+ }
+
+ /// Start a directive (plans and begins execution).
+ pub async fn start_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/start", directive_id))
+ .await
+ }
+
+ /// Pause a directive.
+ pub async fn pause_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/pause", directive_id))
+ .await
+ }
+
+ /// Resume a paused directive.
+ pub async fn resume_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/resume", directive_id))
+ .await
+ }
+
+ /// Stop a directive.
+ pub async fn stop_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/stop", directive_id))
+ .await
+ }
+
+ /// Get the current chain and steps for a directive.
+ pub async fn get_directive_chain(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.get(&format!("/api/v1/directives/{}/chain", directive_id))
+ .await
+ }
+
+ /// Get directive DAG structure for visualization.
+ pub async fn get_directive_graph(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.get(&format!("/api/v1/directives/{}/chain/graph", directive_id))
+ .await
+ }
+
+ /// List events for a directive.
+ pub async fn list_directive_events(
+ &self,
+ directive_id: Uuid,
+ limit: i32,
+ ) -> Result<JsonValue, ApiError> {
+ self.get(&format!(
+ "/api/v1/directives/{}/events?limit={}",
+ directive_id, limit
+ ))
+ .await
+ }
+
+ /// List pending approvals for a directive.
+ pub async fn list_directive_approvals(
+ &self,
+ directive_id: Uuid,
+ ) -> Result<JsonValue, ApiError> {
+ self.get(&format!("/api/v1/directives/{}/approvals", directive_id))
+ .await
+ }
+
+ /// Approve an approval request.
+ pub async fn approve_directive_request(
+ &self,
+ directive_id: Uuid,
+ approval_id: Uuid,
+ response: Option<&str>,
+ ) -> Result<JsonValue, ApiError> {
+ #[derive(serde::Serialize)]
+ #[serde(rename_all = "camelCase")]
+ struct ApprovalRequest<'a> {
+ response: Option<&'a str>,
+ }
+ let req = ApprovalRequest { response };
+ self.post(
+ &format!(
+ "/api/v1/directives/{}/approvals/{}/approve",
+ directive_id, approval_id
+ ),
+ &req,
+ )
+ .await
+ }
+
+ /// Deny an approval request.
+ pub async fn deny_directive_request(
+ &self,
+ directive_id: Uuid,
+ approval_id: Uuid,
+ response: Option<&str>,
+ ) -> Result<JsonValue, ApiError> {
+ #[derive(serde::Serialize)]
+ #[serde(rename_all = "camelCase")]
+ struct ApprovalRequest<'a> {
+ response: Option<&'a str>,
+ }
+ let req = ApprovalRequest { response };
+ self.post(
+ &format!(
+ "/api/v1/directives/{}/approvals/{}/deny",
+ directive_id, approval_id
+ ),
+ &req,
+ )
+ .await
+ }
+}