id_effect 0.4.0

Effect<A, E, R> (sync + async), capability DI, pipe — interpreter-style, no bundled executor
Documentation
# Compute Fabric

Every effect in id_effect passes through **Compute Fabric** at the runtime boundary. You write lazy `Effect` descriptions and declare a [`ResourcePolicy`](../../src/compute/policy.rs); Fabric decides where, when, and how concurrently work runs.

## Declarative policy

```rust,no_run
use id_effect::compute::{ResourcePolicy, MetricMode, MetricPolicy, RebalanceStrategy};

let policy = ResourcePolicy {
  memory: MetricPolicy::new(MetricMode::Max { ceiling: 0.85 }),
  cpu: MetricPolicy::new(MetricMode::Max { ceiling: 1.0 }),
  rebalance: RebalanceStrategy::ThrottleAdmission,
};
```

Memory capped at 85% while CPU may use all cores is:

```rust,no_run
let policy = ResourcePolicy::memory_cap_max_cpu(0.85);
```

Unlimited memory with even CPU spread across workers:

```rust,no_run
let policy = ResourcePolicy::unlimited_memory_cpu_spread(0.25);
```

## Supervisor loop

`ComputeSupervisor` polls `TelemetryEngine` (live sysinfo on host; mock in tests) and compares readings to policy:

1. **Monitor**`TelemetrySnapshot { cpu_pct, mem_pct }`
2. **Admit**`AdmissionController` adjusts permit count
3. **Place** — fibers on `FiberPool`, collections via Rayon, I/O via Tokio
4. **Rebalance** — throttle, shed, scale-out, or scale-in

Each tick emits a [`ComputeEvent`](../../src/observability/tracing.rs) (`SupervisorTick`) for observability alongside [`FiberEvent`](../../src/observability/tracing.rs).

Example: memory at 61% with an 85% ceiling → headroom → admit many fibers. Memory at 86% → throttle admission.

## Installing Fabric

```rust,no_run
use id_effect::{ThreadSleepRuntime, run_fork, succeed};
use id_effect::compute::ComputeFabric;

let fabric = ComputeFabric::memory_cap_max_cpu(0.85);
let rt = ThreadSleepRuntime::with_fabric(fabric);
// fibers spawned via rt use the shared pool + admission
```

[`run_with`](../../src/capability/run.rs) installs a default fabric for the duration of each application run. [`run_blocking`](../../src/runtime/execute.rs) refreshes the thread-local [`AdaptiveContext`](../../src/compute/adaptive.rs) on every entry.

See example `120_compute_fabric_memory_cap.rs`.

## Adaptive parallelism

[`AdaptiveContext`](../../src/compute/adaptive.rs) holds the current admission budget, Rayon thread count, and auto-parallel element threshold. Bulk dispatch via [`parallel_if_profitable`](../../src/compute/dispatch.rs) consults `should_parallelize_current(len)` instead of a fixed 1024 default when Fabric is installed.

- [`configure_rayon_threads`]../../src/compute/rayon_pool.rs — supervisor-sized Rayon pool
- [`CpuSpreadBucket`]../../src/compute/spread.rs — token bucket for `MetricMode::Spread`
- Example: `121_compute_fabric_cpu_spread.rs`

See also [Implicit Parallelism](../part4/ch13-05-parallelism.md) for the full 0.4.0 API surface (`map` / `map_effect` + `*_serial`).

## Effect binds

Independent `~` steps in an `effect!` block may run concurrently when the Effect Dependency Graph finds no data or capability conflict. Dependent steps stay ordered. Use `#[effect(serial)]` to opt out.

Example: `122_compute_fabric_effect_parallel.rs` (`Stream::map_effect` under admission budget).

## Streams and collections

- Element threshold and Rayon pool size follow [`AdaptiveContext`]../../src/compute/adaptive.rs
- [`Stream::map_effect`]../../src/streaming/stream.rs caps concurrent effect mappers from the admission budget
- Pure chunk ops (`map`, `filter`, …) use the same Fabric snapshot via `parallel_if_profitable`
- CPU spread mode caps per-worker share via [`CpuSpreadBucket`]../../src/compute/spread.rs

## Cluster

When local Fabric saturates, `RebalanceStrategy::ScaleOut` builds a [`FabricJobSpec`](../../src/compute/cluster.rs) via [`ComputeSupervisor::scale_out`](../../src/compute/cluster.rs). Convert with `id_effect_jobs::JobSpec::from_fabric` and enqueue on a [`FabricJobRunner`](../../id_effect_jobs/src/runner.rs). Durable cross-node steps hook through [`DistributedStepJournal`](../../id_effect_workflow/src/journal.rs) (stub).

Example: `123_compute_fabric_cluster.rs` (two-node offload with `MemoryJobRunner`).

[`ClusterResourcePolicy`](../../src/compute/cluster.rs) combines global and per-node caps with [`PlacementMode`](../../src/compute/cluster.rs) (`LocalFirst`, `Spread`, `Affinity`).

## Further reading

- [ADR 0007]../../../../docs/adrs/0007-compute-fabric.md
- [ADR 0008]../../../../docs/adrs/0008-implicit-parallelism.md — Fabric-only parallelism (0.4.0)
- [Implicit Parallelism]../part4/ch13-05-parallelism.md
- [Concurrency]./ch09-00-concurrency.md
- [Scheduling]./ch11-00-scheduling.md — temporal vs compute scheduling