effectful 0.3.0

Effect<A, E, R> (sync + async), context/layers, pipe — interpreter-style, no bundled executor
Documentation
# Clock Injection — Testable Time

`retry` and `repeat` use a live clock by default. For deterministic tests, use the explicit-clock variants: `retry_with_clock`, `repeat_with_clock`, and their interruption-aware forms.

## The Clock Trait

```rust,ignore
use std::time::{Duration, Instant};
use effectful::{Effect, Never};

trait Clock {
    fn now(&self) -> Instant;
    fn sleep(&self, duration: Duration) -> Effect<(), Never, ()>;
    fn sleep_until(&self, deadline: Instant) -> Effect<(), Never, ()>;
}
```

`Clock` is monotonic-time oriented. Calendar time for logging is exposed separately through `LiveClock::now_utc()`.

## Production: LiveClock

```rust,ignore
use effectful::{LiveClock, ThreadSleepRuntime};

let live_clock = LiveClock::new(ThreadSleepRuntime);
```

`LiveClock` delegates sleeping and `now()` to a runtime.

## Testing: TestClock

```rust,ignore
use std::time::{Duration, Instant};
use effectful::{Clock, TestClock};

let start = Instant::now();
let clock = TestClock::new(start);

assert_eq!(clock.now(), start);

clock.advance(Duration::from_secs(60));
assert_eq!(clock.now(), start + Duration::from_secs(60));
```

`TestClock` records pending sleeps. Advancing or setting time drops pending sleeps whose deadlines have elapsed.

## Test Example

```rust,ignore
use std::sync::{Arc, atomic::{AtomicU32, Ordering}};
use std::time::{Duration, Instant};
use effectful::{Schedule, TestClock, retry_with_clock, run_blocking};

let clock = TestClock::new(Instant::now());
let attempts = Arc::new(AtomicU32::new(0));

let effect = retry_with_clock(
    {
        let attempts = attempts.clone();
        move || failing_operation(attempts.clone())
    },
    Schedule::exponential(Duration::from_secs(1)).compose(Schedule::recurs(3)),
    clock.clone(),
    None,
);

let result = run_blocking(effect, ());
assert!(result.is_err());
assert_eq!(attempts.load(Ordering::Relaxed), 4); // initial + 3 retries
```

This test runs without sleeping in real time because `TestClock::sleep` only records deadlines.

## Clock as a Service

For application logic that needs time directly, model the clock as a service in your environment. The scheduling helpers accept a clock value explicitly; your own services can do the same.