summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers/directives.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/server/handlers/directives.rs')
-rw-r--r--makima/src/server/handlers/directives.rs85
1 files changed, 68 insertions, 17 deletions
diff --git a/makima/src/server/handlers/directives.rs b/makima/src/server/handlers/directives.rs
index d1edf7e..01c4659 100644
--- a/makima/src/server/handlers/directives.rs
+++ b/makima/src/server/handlers/directives.rs
@@ -18,7 +18,10 @@ use crate::db::models::{
OrderListResponse,
};
use crate::db::repository;
-use crate::orchestration::directive::{build_cleanup_prompt, build_order_pickup_prompt};
+use crate::orchestration::directive::{
+ build_cleanup_prompt, build_order_pickup_prompt, classify_goal_change,
+ try_interrupt_planner_with_goal_edit, GoalChangeKind, GoalEditInterruptResult,
+};
use crate::server::auth::Authenticated;
use crate::server::messages::ApiError;
use crate::server::state::SharedState;
@@ -827,31 +830,79 @@ pub async fn update_goal(
.into_response();
};
- // Save old goal to history before overwriting (best-effort)
- match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
- Ok(Some(current)) => {
- if let Err(e) = repository::save_directive_goal_history(pool, id, &current.goal).await
- {
- tracing::warn!(
- directive_id = %id,
- error = %e,
- "Failed to save goal history before update — continuing with goal update"
- );
- }
- }
- Ok(None) => {
- // Directive not found — update_directive_goal will handle this
- }
+ // Fetch the current directive so we can:
+ // 1. Save the old goal to history (best-effort).
+ // 2. Decide whether to fire a goal-edit interrupt at a running planner.
+ let current = match repository::get_directive_for_owner(pool, auth.owner_id, id).await {
+ Ok(Some(d)) => Some(d),
+ Ok(None) => None,
Err(e) => {
tracing::warn!(
directive_id = %id,
error = %e,
"Failed to fetch current directive for goal history — continuing with goal update"
);
+ None
+ }
+ };
+
+ // Save old goal to history before overwriting (best-effort).
+ if let Some(ref current) = current {
+ if let Err(e) = repository::save_directive_goal_history(pool, id, &current.goal).await {
+ tracing::warn!(
+ directive_id = %id,
+ error = %e,
+ "Failed to save goal history before update — continuing with goal update"
+ );
+ }
+ }
+
+ // Goal-edit interrupt cycle: if a planner task is currently running for
+ // this directive AND the goal change classifies as 'small', interrupt the
+ // running planner via SendMessage instead of clearing it (which would
+ // trigger a fresh replan on the next orchestrator tick).
+ let mut interrupted = false;
+ if let Some(ref current) = current {
+ if current.orchestrator_task_id.is_some()
+ && classify_goal_change(&current.goal, &req.goal) == GoalChangeKind::Small
+ {
+ match try_interrupt_planner_with_goal_edit(
+ pool,
+ &state,
+ id,
+ &current.goal,
+ &req.goal,
+ )
+ .await
+ {
+ Ok(GoalEditInterruptResult::Sent) => {
+ interrupted = true;
+ }
+ Ok(GoalEditInterruptResult::Skipped) => {}
+ Err(e) => {
+ tracing::warn!(
+ directive_id = %id,
+ error = %e,
+ "Goal-edit interrupt attempt errored — falling back to replan"
+ );
+ }
+ }
}
}
- match repository::update_directive_goal(pool, auth.owner_id, id, &req.goal).await {
+ // If we successfully interrupted a running planner, persist the new goal
+ // WITHOUT clearing the orchestrator task — the planner will react to the
+ // SendMessage and adjust in-flight. Otherwise, fall through to the normal
+ // path which clears orchestrator_task_id and lets phase_replanning kick
+ // in on the next tick.
+ let update_result = if interrupted {
+ repository::update_directive_goal_keep_orchestrator(pool, auth.owner_id, id, &req.goal)
+ .await
+ } else {
+ repository::update_directive_goal(pool, auth.owner_id, id, &req.goal).await
+ };
+
+ match update_result {
Ok(Some(directive)) => Json(directive).into_response(),
Ok(None) => (
StatusCode::NOT_FOUND,