---
plan_id: "03-01"
phase: "03"
phase_name: "Rate Limiter Dynamic Ceiling"
wave: 1
dependencies: []
files_modified:
  - crates/opendeviationbar-providers/src/binance/rate_limiter.rs
requirements:
  - PARA-03
autonomous: true
tasks: 2
---

# Plan 03-01: Rate Limiter Dynamic Ceiling

## Task 1: Convert ceiling to AtomicU32 + add set_ceiling()

<read_first>

- crates/opendeviationbar-providers/src/binance/rate_limiter.rs (full file)
  </read_first>

<acceptance_criteria>

- `grep -q "ceiling: AtomicU32" crates/opendeviationbar-providers/src/binance/rate_limiter.rs`
- `grep -q "pub fn set_ceiling" crates/opendeviationbar-providers/src/binance/rate_limiter.rs`
- `grep -q "self.ceiling.load(Ordering::Relaxed)" crates/opendeviationbar-providers/src/binance/rate_limiter.rs`
- `grep -q "self.ceiling.store" crates/opendeviationbar-providers/src/binance/rate_limiter.rs`
- `cargo check -p opendeviationbar-providers` passes
  </acceptance_criteria>

<action>
1. Change `ceiling: u32` to `ceiling: AtomicU32` in `AdaptiveRateLimiter` struct
2. Update `new()`: `ceiling: AtomicU32::new(0)`
3. Update `new_with_ceiling()`: `ceiling: AtomicU32::new(ceiling)`
4. Update `usable_budget()`: read via `self.ceiling.load(Ordering::Relaxed)`
5. Add `pub fn set_ceiling(&self, new_ceiling: u32)` that calls `self.ceiling.store(new_ceiling, Ordering::Relaxed)`
</action>

## Task 2: Unit test for set_ceiling

<read_first>

- crates/opendeviationbar-providers/src/binance/rate_limiter.rs (tests module)
  </read_first>

<acceptance_criteria>

- `grep -q "test_set_ceiling_updates_limit" crates/opendeviationbar-providers/src/binance/rate_limiter.rs`
- `cargo test -p opendeviationbar-providers -- test_set_ceiling_updates_limit` passes
  </acceptance_criteria>

<action>
1. Add `#[test] fn test_set_ceiling_updates_limit` to the `tests` module
2. Create limiter with `new_with_ceiling(6000, 0.20, 3000)` — budget should be 3000
3. Call `set_ceiling(4800)` — budget should be 4800
4. Call `set_ceiling(0)` — budget should be 4800 (full global budget, no ceiling)
5. Assert each transition with `assert_eq!(limiter.usable_budget(), expected)`
</action>
