hive-c0re stops owning scheduled prompts; agents lose the schedule tools
Scheduled prompts are swarm-level now (swarmctl schedule, #4936). The hive keeps the rows it has only so the operator can copy them over. - hive-c0re no longer fires anything: the scheduled-prompts worker is gone, and with it every create/edit/cancel/pause/resume/fire path (dashboard endpoints, agent-socket verbs, the SchedulePrompt approval kind and its approve handler). The store keeps open + list; GET /api/schedules stays. - A v6 approvals migration drops schedule_prompt rows. - Nothing submits approvals any more, so the submit path goes too: Approvals::submit_kind (now a test fixture), the ApprovalAdded dashboard event and its emitter. - Coordinator::push_todo, whose only caller was the worker, goes, and with it hive_host_sock::agent_todo_socket and hive-c0re's hive-agent-sock / hive-sock-client deps. - The schedules_changed dashboard event goes; nothing mutates the rows. - Agents lose request_schedule_prompt, cancel_schedule, fire_schedule_now, edit_schedule and list_schedules, the `scheduling` tool group, their system-prompt line and their transcript formatters. `remind` is unchanged.
This commit is contained in:
parent
7d80f5146d
commit
c3a54bbdd0
31 changed files with 273 additions and 3124 deletions
|
|
@ -1,10 +1,9 @@
|
|||
//! Approval queue. Requests are submitted by an agent
|
||||
//! (`RequestSchedulePrompt`); the user approves/denies via the host admin
|
||||
//! CLI; on approval the host runs the corresponding action.
|
||||
//! Approval queue. The user approves/denies via the host admin CLI; on
|
||||
//! approval the host runs the corresponding action.
|
||||
//!
|
||||
//! `UpdateMetaInputs` rows are legacy: the MCP tool that queued them was
|
||||
//! removed and nothing produces the kind any more. The variant and
|
||||
//! its (de)serialization stay so pre-existing rows still read back.
|
||||
//! Nothing submits approvals any more. `UpdateMetaInputs`, the only kind
|
||||
//! left, is legacy: the MCP tool that queued it was removed. The variant
|
||||
//! and its (de)serialization stay so pre-existing rows still read back.
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
|
|
@ -64,6 +63,13 @@ const MIGRATIONS: &[Migration] = &[
|
|||
sql: "DELETE FROM approvals WHERE kind = 'merge_config_pr'",
|
||||
adds_column: None,
|
||||
},
|
||||
// v6: drop `schedule_prompt` rows. Scheduled prompts are swarm-level, so
|
||||
// no approval of that kind can be acted on, and `row_to_approval` rejects
|
||||
// the kind.
|
||||
Migration {
|
||||
sql: "DELETE FROM approvals WHERE kind = 'schedule_prompt'",
|
||||
adds_column: None,
|
||||
},
|
||||
];
|
||||
|
||||
pub struct Approvals {
|
||||
|
|
@ -81,7 +87,8 @@ impl Approvals {
|
|||
})
|
||||
}
|
||||
|
||||
/// Insert a new pending approval row.
|
||||
/// Test fixture: insert a new pending approval row.
|
||||
#[cfg(test)]
|
||||
pub fn submit_kind(
|
||||
&self,
|
||||
agent: &str,
|
||||
|
|
@ -354,7 +361,6 @@ fn row_to_approval(row: &rusqlite::Row<'_>) -> rusqlite::Result<Approval> {
|
|||
let kind: String = row.get(2)?;
|
||||
let kind = match kind.as_str() {
|
||||
"update_meta_inputs" => ApprovalKind::UpdateMetaInputs,
|
||||
"schedule_prompt" => ApprovalKind::SchedulePrompt,
|
||||
other => {
|
||||
return Err(rusqlite::Error::FromSqlConversionFailure(
|
||||
2,
|
||||
|
|
@ -404,7 +410,6 @@ fn row_to_approval(row: &rusqlite::Row<'_>) -> rusqlite::Result<Approval> {
|
|||
fn kind_from_str(s: &str) -> Result<ApprovalKind> {
|
||||
Ok(match s {
|
||||
"update_meta_inputs" => ApprovalKind::UpdateMetaInputs,
|
||||
"schedule_prompt" => ApprovalKind::SchedulePrompt,
|
||||
other => bail!("unknown approval kind '{other}'"),
|
||||
})
|
||||
}
|
||||
|
|
@ -421,17 +426,6 @@ mod tests {
|
|||
(dir, path, db)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_kinds_all_listed() {
|
||||
let (_dir, _path, db) = open_temp();
|
||||
db.submit_kind("b", ApprovalKind::SchedulePrompt, "", None, "b")
|
||||
.unwrap();
|
||||
db.submit_kind("c", ApprovalKind::UpdateMetaInputs, "[]", None, "c")
|
||||
.unwrap();
|
||||
let pending = db.pending().expect("pending");
|
||||
assert_eq!(pending.len(), 2, "both kinds must be visible");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mark_cancelled_transitions_pending_row() {
|
||||
// Manager withdraws a pending approval. Row leaves pending(),
|
||||
|
|
@ -441,8 +435,8 @@ mod tests {
|
|||
let id = db
|
||||
.submit_kind(
|
||||
"bitburner",
|
||||
ApprovalKind::SchedulePrompt,
|
||||
"{}",
|
||||
ApprovalKind::UpdateMetaInputs,
|
||||
"[]",
|
||||
Some("test"),
|
||||
"bitburner",
|
||||
)
|
||||
|
|
@ -464,7 +458,7 @@ mod tests {
|
|||
// final — re-cancelling errors instead of silently overwriting.
|
||||
let (_dir, _path, db) = open_temp();
|
||||
let id = db
|
||||
.submit_kind("a", ApprovalKind::SchedulePrompt, "{}", None, "a")
|
||||
.submit_kind("a", ApprovalKind::UpdateMetaInputs, "[]", None, "a")
|
||||
.unwrap();
|
||||
db.mark_cancelled(id, "manager").expect("first cancel");
|
||||
let err = db
|
||||
|
|
@ -479,7 +473,7 @@ mod tests {
|
|||
// whole list — collect_lenient skips it instead of failing.
|
||||
let (_dir, path, db) = open_temp();
|
||||
let good = db
|
||||
.submit_kind("good", ApprovalKind::SchedulePrompt, "{}", None, "good")
|
||||
.submit_kind("good", ApprovalKind::UpdateMetaInputs, "[]", None, "good")
|
||||
.unwrap();
|
||||
let raw = Connection::open(&path).unwrap();
|
||||
raw.execute(
|
||||
|
|
@ -502,14 +496,20 @@ mod tests {
|
|||
// fall back to the root agent.
|
||||
let (_dir, path, db) = open_temp();
|
||||
let id = db
|
||||
.submit_kind("child", ApprovalKind::SchedulePrompt, "{}", None, "parent")
|
||||
.submit_kind(
|
||||
"child",
|
||||
ApprovalKind::UpdateMetaInputs,
|
||||
"[]",
|
||||
None,
|
||||
"parent",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(db.submitter_of(id).unwrap().as_deref(), Some("parent"));
|
||||
|
||||
let raw = Connection::open(&path).unwrap();
|
||||
raw.execute(
|
||||
"INSERT INTO approvals (agent, kind, commit_ref, requested_at, status)
|
||||
VALUES ('old', 'schedule_prompt', '', 0, 'pending')",
|
||||
VALUES ('old', 'update_meta_inputs', '', 0, 'pending')",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
|
|
@ -524,7 +524,7 @@ mod tests {
|
|||
fn a_pre_v5_merge_config_pr_row_is_dropped_on_open() {
|
||||
let (_dir, path, db) = open_temp();
|
||||
let kept = db
|
||||
.submit_kind("iris", ApprovalKind::SchedulePrompt, "{}", None, "iris")
|
||||
.submit_kind("iris", ApprovalKind::UpdateMetaInputs, "[]", None, "iris")
|
||||
.unwrap();
|
||||
drop(db);
|
||||
let raw = Connection::open(&path).unwrap();
|
||||
|
|
@ -546,4 +546,30 @@ mod tests {
|
|||
assert!(db.recent_resolved(10).unwrap().is_empty());
|
||||
assert!(db.get(old).unwrap().is_none());
|
||||
}
|
||||
|
||||
/// A database written before v6 still holds `schedule_prompt` rows.
|
||||
/// Opening it drops them and keeps every other row.
|
||||
#[test]
|
||||
fn a_pre_v6_schedule_prompt_row_is_dropped_on_open() {
|
||||
let (_dir, path, db) = open_temp();
|
||||
let kept = db
|
||||
.submit_kind("iris", ApprovalKind::UpdateMetaInputs, "[]", None, "iris")
|
||||
.unwrap();
|
||||
drop(db);
|
||||
let raw = Connection::open(&path).unwrap();
|
||||
raw.execute_batch(
|
||||
"INSERT INTO approvals (agent, kind, commit_ref, requested_at, status)
|
||||
VALUES ('iris', 'schedule_prompt', '{}', 0, 'pending');
|
||||
UPDATE schema_versions SET version = 5 WHERE store = 'approvals';",
|
||||
)
|
||||
.unwrap();
|
||||
let old: i64 = raw.last_insert_rowid();
|
||||
drop(raw);
|
||||
|
||||
let db = Approvals::open(&path).expect("a pre-v6 db opens");
|
||||
let pending = db.pending().expect("pending");
|
||||
assert_eq!(pending.len(), 1);
|
||||
assert_eq!(pending[0].id, kept);
|
||||
assert!(db.get(old).unwrap().is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! Sqlite-backed host-side stores (broker, approval / schedule queues,
|
||||
//! build logs, power intent) plus the shared connection open/migration
|
||||
//! Sqlite-backed host-side stores (broker, approval queue, read-only
|
||||
//! scheduled prompts, build logs, power intent) plus the shared connection open/migration
|
||||
//! helper (`db`). Each submodule is re-exported at the crate root, so
|
||||
//! `crate::broker::…` etc. keep working unchanged.
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue