# Bug: range() с reversed=true возвращает (start, end] вместо [start, end)

**Status**: исправлен
**Severity**: высокий — range возвращает неправильные записи, сдвиг на 1 в обоих границах
**Затронуты**: `ConstTree::range()`, `VarTree::range()`, `TypedTree::range()`, `ZeroTree::range()` — любое дерево с `reversed=true` (дефолт)
**Не затронуты**: `iter()`, `prefix_iter()`, деревья с `reversed=false`

## Симптомы

`range(&5, &10)` с `reversed=true` возвращает ключи `{10, 9, 8, 7, 6}` вместо `{9, 8, 7, 6, 5}`.
Start-ключ исключается, end-ключ включается — ровно наоборот от контракта `[start, end)`.

```
// Ожидание: [start, end) = [5, 10) → {9, 8, 7, 6, 5}
// Реальность: (start, end] = (5, 10] → {10, 9, 8, 7, 6}
```

## Причина

Две ошибки в reversed-ветке `range()`:

### 1. Неправильные bounds для ConstIter

```rust
// Было:
end: Bound::Excluded(*start),   // check_end reversed: key > start → исключает start
start: Bound::Included(*end),   // check_start reversed: key <= end → включает end

// Стало:
end: Bound::Included(*start),   // check_end reversed: key >= start → включает start
start: Bound::Excluded(*end),   // check_start reversed: key < end → исключает end
```

`check_end` вызывается в `Iterator::next()` (forward — descending для reversed).
С `Excluded(start)` проверка `key > start` ложна для `key == start`, поэтому start исключался.
С `Included(start)` проверка `key >= start` истинна для `key == start` — start включён.

Аналогично `check_start` с `Included(end)` включал end, а должен исключать.

### 2. Неправильные front/back указатели

```rust
// Было:
let front = self.index.find_first_ge(end.as_bytes(), &guard);
let back = self.index.find_last_lt(start.as_bytes());
```

В reversed skip list `find_first_ge(end)` находит первый узел с natural key <= end.
Для `end=10` это узел с ключом 10 — но end исключён из диапазона, нужен узел 9.

`find_last_lt(start)` находит последний узел с natural key > start.
Для `start=5` это узел 6 — но start включён, нужен узел 5.

```rust
// Стало:
// front: skip past end key if it exists (end is exclusive)
let ge_end = self.index.find_first_ge(end.as_bytes(), &guard);
let front = if node_at(ge_end).key == end {
    ge_end.next()  // advance past excluded end
} else {
    ge_end         // already < end, correct
};

// back: use start key if it exists (start is inclusive)
let ge_start = self.index.find_first_ge(start.as_bytes(), &guard);
let back = if node_at(ge_start).key == start {
    ge_start       // start exists, use it
} else {
    find_last_lt(start)  // fallback: last node > start
};
```

## Почему не ловилось

Тесты итераторов использовали `reversed=false` (явно или через `Config::test()` до того,
как дефолт `reversed` стал `true`). Range с `reversed=false` работал корректно —
баг только в reversed-ветке. Тесты `reversed=true` покрывали `iter()` и `prefix_iter()`,
но не `range()`.

## Исправление

Файлы:
- `armdb/src/const_tree.rs` — `ConstTree::range()`
- `armdb/src/var_tree.rs` — `VarTree::range()`
- `armdb/src/typed_tree.rs` — `TypedTree::range()`

`ZeroTree::range()` делегирует к `ConstTree::range()` — исправлен транзитивно.

Тесты range теперь запускаются с `Config::test()` (`reversed=true`), proptest проверяет
оба режима (reversed + ascending) × 256 случайных сценариев каждый.
