From 09e362690ed2d1ab7f79163e134894305aa0f178 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Sat, 3 Oct 2026 01:56:52 -0700 Subject: [PATCH] docs: M5d-2 hold returns Busy, not Result<(), ()> clippy result_unit_err is denied by the gate. Context::hold uses a Busy unit struct; the given tests still only expect Ok. Co-Authored-By: Grok 4.6 --- .../M5d2/06-loopd-scheduler-heartbeat.md | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md b/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md index 4012084..3b16fdf 100644 --- a/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md +++ b/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md @@ -40,9 +40,9 @@ Tests call `tick(ctx, now)` with injected time. No sleeps (T27). ## Facts you need -- **`Context::hold`.** Same insert as `handle`: if the id is already in `busy`, `Err(())`; +- **`Context::hold`.** Same insert as `handle`: if the id is already in `busy`, `Err(Busy)`; else insert and return `Held`. `Held` must be `pub` so the given test can keep it. `Drop` - already removes the id. + already removes the id. Do not return `Result<_, ()>`: clippy `result_unit_err` is denied. - **Due.** `every_s == 0` → no heartbeat work. Else `now.unix_millis().saturating_sub(last.unix_millis()) >= every_s.saturating_mul(1000)` where `last` is `last_heartbeat.unwrap_or(started)`. `07:00` to `07:01` with `every_s = 60` is due. `07:01` then `07:01:30` is not. @@ -101,17 +101,24 @@ Tests call `tick(ctx, now)` with injected time. No sleeps (T27). ### `crates/loopd/src/channel.rs` -**C1.** `struct Held` becomes `pub struct Held`. +**C1.** `struct Held` becomes `pub struct Held`. Immediately after that struct (before +`impl Drop for Held`), add: + +```rust +/// The session is already in the busy set. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Busy; +``` **C2.** Inside `impl Context`, after `new`, add: ```rust - /// Mark `id` busy. `Err(())` if it already is. Tests and the scheduler use this so a skip + /// Mark `id` busy. `Err(Busy)` if it already is. Tests and the scheduler use this so a skip /// is not a sleep (T27). - pub fn hold(&self, id: SessionId) -> Result, ()> { + pub fn hold(&self, id: SessionId) -> Result, Busy> { let mut busy = self.busy.lock().unwrap_or_else(|p| p.into_inner()); if busy.contains(&id) { - return Err(()); + return Err(Busy); } busy.insert(id.clone()); drop(busy); @@ -206,7 +213,8 @@ pass so it runs once. - The branch check fails; a "before" block is not found exactly once; step 2 differs; `cargo check` warns; a test fails; the gate count is not 1551; you sleep in a test; you post to - Mattermost; you send `slots.main`; you set a speaker; you `chmod 000`; `Held` stays private. + Mattermost; you send `slots.main`; you set a speaker; you `chmod 000`; `Held` stays private; + `hold` returns `Result<_, ()>`. ## What the given tests pin