Compare commits

..
2 changed files with 35 additions and 80 deletions

View file

@ -585,10 +585,7 @@ async fn serve_loop<S: Surface>(
}
} {
RecvOutcome::Message(first) => first,
RecvOutcome::LocalTodo => {
tracing::debug!("todo wake consumed, sending synthetic todo message");
synthetic_todo_message()
}
RecvOutcome::LocalTodo => synthetic_todo_message(),
RecvOutcome::Empty => {
// Idle: no message this poll. Service a queued operator
// `/compact` here so it runs even when no turn is driving

View file

@ -170,21 +170,44 @@ fn dispatch(
key,
summary,
source,
} => upsert_todo(
store,
wake,
&subsystem,
key.as_deref(),
&summary,
source.as_deref(),
),
} => match store.upsert(&subsystem, key.as_deref(), &summary, source.as_deref()) {
Ok((_, changed)) => {
if changed {
wake.notify_one();
}
Response::Ok
}
Err(e) => err(&e),
},
Request::ClearTodo {
subsystem,
key,
all,
} => clear_todo(store, &subsystem, key.as_deref(), all),
Request::ListTodos { subsystem } => list_todos(store, subsystem.as_deref()),
Request::MarkTodoDone { id } => mark_todo_done(store, id),
} => {
let result = if all {
store.clear_subsystem(&subsystem)
} else {
store.clear(&subsystem, key.as_deref())
};
match result {
Ok(count) => Response::Acked {
count: u64::try_from(count).unwrap_or(0),
},
Err(e) => err(&e),
}
}
Request::ListTodos { subsystem } => match store.list(subsystem.as_deref()) {
Ok(todos) => Response::LooseEnds {
loose_ends: todos.into_iter().map(to_loose_end).collect(),
},
Err(e) => err(&e),
},
Request::MarkTodoDone { id } => match store.mark_done(id) {
Ok(count) => Response::Acked {
count: u64::try_from(count).unwrap_or(0),
},
Err(e) => err(&e),
},
Request::StoreReminder {
message,
timing,
@ -237,71 +260,6 @@ fn dispatch(
}
}
/// `UpsertTodo` handler: writes/refreshes a todo row, logs the outcome, and
/// fires `wake` on a new-or-changed upsert so the serve loop runs a turn.
fn upsert_todo(
store: &Todos,
wake: &Notify,
subsystem: &str,
key: Option<&str>,
summary: &str,
source: Option<&str>,
) -> Response {
match store.upsert(subsystem, key, summary, source) {
Ok((id, changed)) => {
tracing::debug!(%subsystem, ?key, id, changed, "todo upsert");
if changed {
wake.notify_one();
}
Response::Ok
}
Err(e) => err(&e),
}
}
/// `ClearTodo` handler: drops one keyed todo, or every todo in `subsystem`
/// when `all` is set.
fn clear_todo(store: &Todos, subsystem: &str, key: Option<&str>, all: bool) -> Response {
let result = if all {
store.clear_subsystem(subsystem)
} else {
store.clear(subsystem, key)
};
match result {
Ok(count) => {
tracing::debug!(%subsystem, ?key, all, count, "todo clear");
Response::Acked {
count: u64::try_from(count).unwrap_or(0),
}
}
Err(e) => err(&e),
}
}
/// `ListTodos` handler: read-only, so no debug logging — not relevant to
/// diagnosing wake behaviour.
fn list_todos(store: &Todos, subsystem: Option<&str>) -> Response {
match store.list(subsystem) {
Ok(todos) => Response::LooseEnds {
loose_ends: todos.into_iter().map(to_loose_end).collect(),
},
Err(e) => err(&e),
}
}
/// `MarkTodoDone` handler: marks a single todo done by id.
fn mark_todo_done(store: &Todos, id: i64) -> Response {
match store.mark_done(id) {
Ok(count) => {
tracing::debug!(id, count, "todo mark-done");
Response::Acked {
count: u64::try_from(count).unwrap_or(0),
}
}
Err(e) => err(&e),
}
}
/// `Request::Compact` handler: gate on context usage, then queue the same
/// deferred `compact_pending` flag the operator's `/compact` button sets.
/// Mirrors `hive-agent::web_ui::actions::post_compact` but reachable from