hive-agent: key the boot-announce todo so it dedups across boots
This commit is contained in:
parent
cd61ea4a37
commit
f33a1ed660
1 changed files with 49 additions and 6 deletions
|
|
@ -18,11 +18,16 @@
|
|||
//! (the old first-spawn message didn't reliably fire anyway, so unifying
|
||||
//! the two loses nothing).
|
||||
//!
|
||||
//! Keyless on purpose: each boot is a genuinely new occurrence, not a
|
||||
//! reconciled condition like `disk_watch`'s, so there's nothing to dedup
|
||||
//! *against* within one store — `Todos::upsert`'s keyless path always
|
||||
//! inserts a fresh row and always reports `changed`, which is exactly
|
||||
//! "wake me, this happened" with no extra bookkeeping.
|
||||
//! Keyed on a constant `"boot"` key, not keyless: the store (`Todos`,
|
||||
//! backed by `paths::state_db()`, a sqlite file) outlives the process, so
|
||||
//! a keyless insert would never dedup across boots — N harness starts
|
||||
//! while the agent hasn't acked the previous row would leave N identical
|
||||
//! rows, exactly the restart-storm this module replaces. With a constant
|
||||
//! key and the summary text constant too, `Todos::upsert`'s content-
|
||||
//! unchanged path makes a boot while the previous notice is still pending
|
||||
//! a no-op (no second wake), and `reopen_if_acked = true` makes a boot
|
||||
//! after the agent acked it re-open the same row and wake again. One row,
|
||||
//! ever.
|
||||
|
||||
use tokio::sync::Notify;
|
||||
|
||||
|
|
@ -31,6 +36,10 @@ use crate::todos::Todos;
|
|||
/// Producing subsystem marker for the boot-announce todo row.
|
||||
const SUBSYSTEM: &str = "harness";
|
||||
|
||||
/// Dedup key for the boot-announce row — constant, so every boot targets
|
||||
/// the same row instead of inserting a fresh one. See module doc.
|
||||
const KEY: &str = "boot";
|
||||
|
||||
/// Push the "you were just (re)started" todo and wake the turn loop.
|
||||
/// Best-effort: a store error is logged, never fatal to boot.
|
||||
pub fn announce(label: &str, todos: &Todos, wake: &Notify) {
|
||||
|
|
@ -40,8 +49,42 @@ pub fn announce(label: &str, todos: &Todos, wake: &Notify) {
|
|||
off — claude's `--continue` session is intact, so prior context is \
|
||||
still in your window."
|
||||
);
|
||||
match todos.upsert(SUBSYSTEM, None, &summary, None, false) {
|
||||
match todos.upsert(SUBSYSTEM, Some(KEY), &summary, None, true) {
|
||||
Ok(_) => wake.notify_one(),
|
||||
Err(e) => tracing::warn!(error = ?e, "boot_announce: todo upsert failed"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn two_boots_without_an_ack_leave_one_row() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let todos = Todos::open(&dir.path().join("state.sqlite")).expect("open");
|
||||
let wake = Notify::new();
|
||||
|
||||
announce("a", &todos, &wake);
|
||||
let rows = todos.list(Some(SUBSYSTEM)).expect("list");
|
||||
assert_eq!(rows.len(), 1);
|
||||
let id = rows[0].id;
|
||||
|
||||
// A second boot before the first notice is acked: same row, no
|
||||
// duplicate.
|
||||
announce("a", &todos, &wake);
|
||||
let rows = todos.list(Some(SUBSYSTEM)).expect("list");
|
||||
assert_eq!(rows.len(), 1);
|
||||
assert_eq!(rows[0].id, id);
|
||||
|
||||
// Ack it, then boot again: the row re-opens (still the same row,
|
||||
// un-acked again), not a second insert.
|
||||
todos.mark_done(id).expect("mark_done");
|
||||
assert!(todos.list(Some(SUBSYSTEM)).expect("list").is_empty());
|
||||
|
||||
announce("a", &todos, &wake);
|
||||
let rows = todos.list(Some(SUBSYSTEM)).expect("list");
|
||||
assert_eq!(rows.len(), 1);
|
||||
assert_eq!(rows[0].id, id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue