From 2bf90c9db203140c6bdc908449eb851e20e6503b Mon Sep 17 00:00:00 2001 From: damocles Date: Tue, 26 May 2026 16:21:57 +0200 Subject: [PATCH] scheduled prompts: refresh stale docs for target edits (#478) --- hive-ag3nt/src/mcp.rs | 9 ++++++--- hive-c0re/src/scheduled_prompts.rs | 28 +++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index c5912c5c..1a28c02b 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -1368,9 +1368,12 @@ impl ManagerServer { description = "Edit an existing scheduled prompt's mutable fields (#474). Pass only \ the fields you want to change — anything omitted keeps its current value. Editable: \ `body`, `description`, `interval_seconds` (positive only via this tool; flipping \ - recurring→one-shot is operator-only via the dashboard), `next_fire_at_unix`. \ - Targets are immutable: per-target last-result history is keyed on them. To change \ - the recipient list, cancel and submit a new schedule. \n\n\ + recurring→one-shot is operator-only via the dashboard), `next_fire_at_unix`, and \ + the target set via `targets_add` / `targets_remove` (#478). Both target lists are \ + applied in the same transaction with removes-before-adds, so a single edit can \ + swap a target atomically. Re-adding a previously-removed target starts a fresh \ + per-target history (drops the tombstone). Draining all targets auto-cancels the \ + parent schedule. \n\n\ Authorization mirrors `cancel_schedule` / `fire_schedule_now`: you can edit your \ own schedules + any owned by a sub-agent in your subtree per topology.json. \ Refuses cancelled schedules (the row's terminal — submit a fresh one)." diff --git a/hive-c0re/src/scheduled_prompts.rs b/hive-c0re/src/scheduled_prompts.rs index ccd41909..8b958452 100644 --- a/hive-c0re/src/scheduled_prompts.rs +++ b/hive-c0re/src/scheduled_prompts.rs @@ -360,15 +360,25 @@ impl ScheduledPrompts { } /// Partial-update an existing schedule's mutable fields (#474). - /// Every field is `Option<_>`; `None` means "leave the existing - /// value alone", `Some(_)` means "set it to this." Refuses - /// cancelled rows (no point editing a tombstone — operator can - /// just submit a new schedule). Refuses `interval_seconds = Some(0)` - /// — same rule as submit-time validation. Targets stay - /// immutable: per-target last-result history is keyed on - /// (schedule_id, target); changing the target set would orphan - /// or duplicate history rows. Operator workaround for "I want - /// different targets" is cancel-target + submit a new schedule. + /// Every scalar field is `Option<_>`; `None` means "leave the + /// existing value alone", `Some(_)` means "set it to this." + /// Refuses cancelled rows (no point editing a tombstone — + /// operator can just submit a new schedule). Refuses + /// `interval_seconds = Some(0)` — same rule as submit-time + /// validation. + /// + /// Targets are mutable via `targets_remove` + `targets_add` + /// (#478 fast-follow). Both are processed in the same + /// transaction, with **removes before adds** so a single PATCH + /// can swap a target without ever leaving the schedule + /// target-less mid-tx. Remove writes a tombstone via + /// `COALESCE(cancelled_at_unix, ?1)` so it's idempotent. Add + /// uses `INSERT OR REPLACE` so re-adding a previously-removed + /// target drops the tombstone and resets per-target history + /// (fresh start — option C from the iris design thread). If + /// the post-tx active-target count is zero the parent schedule + /// is auto-cancelled — the worker would otherwise spin forever + /// firing nothing. pub fn update(&self, id: i64, patch: UpdateSchedule) -> Result<()> { // Pre-flight: row must exist and not be cancelled. let mut conn = self.conn.lock().unwrap();