# 🔥 Tier 1.5 — Lifecycle & Reliability

Startup, shutdown, hot-reload, fail-safe — всё что влияет на жизненный цикл сервера.

> **Примечание:** Bounded Session Storage (LRU/TTL) — **уже реализовано** в `session.py`
> (`SessionManager` с `ttl_seconds`, `max_sessions`, `_evict_expired`, `_evict_oldest`).
> Перенесено в ROADMAP как completed.

### ~~Bounded Session Storage (LRU/TTL)~~ ✅ DONE

> Реализовано в `session.py`. `SessionManager` имеет `ttl_seconds=3600`, `max_sessions=1000`,
> `_evict_expired()`, `_evict_oldest()`. LRU + TTL. Закрыто.

### Graceful Shutdown & Signal Handling 🔴 `v1.0-blocker`

При `SIGTERM`/`SIGINT` сервер должен: flush трейсов, завершить pending approvals, дождаться in-flight requests. Без этого — потеря данных при каждом деплое в Docker/K8s. **Сейчас SIGTERM = мгновенная смерть.**

**Важно:** текущий `lifespan()` в `app.py` останавливает только file watcher, но **не останавливает Telegram poller** (`TelegramApprovalBackend.stop()` нигде не вызывается). Daemon thread умирает без cleanup → pending approvals теряются.

- **Усилия**: Маленькие (~40 строк, lifespan hooks + backend.stop())
- **Ценность**: 🔥🔥🔥 — обязательно для контейнерного деплоя

### Trace Flush on Shutdown 🔴 `v1.0-blocker`

`TraceRecorder` буферизирует записи (`batch_size=100`), flush при заполнении или явном вызове. При shutdown (`lifespan()`) вызывается только `stop_watching()` — **`tracer.flush()` нигде не вызывается**. До 99 аудит-записей теряются при каждом деплое.

Связано с Graceful Shutdown, но **отдельная проблема**: даже с graceful shutdown, если `lifespan()` не вызывает `tracer.flush()` — данные пропадут.

```python
# lifespan() в app.py — СЕЙЧАС:
yield
engine.stop_watching()
# НЕТ: engine._tracer.flush()

# НУЖНО:
yield
engine.stop_watching()
if engine._tracer:
    engine._tracer.flush()  # ← 1 строка, спасает аудит-данные
```

- **Усилия**: 1 строка
- **Ценность**: 🔥🔥🔥 — потеря аудит-данных при каждом деплое, нарушает compliance

### Config Validation на старте 🔴 `v1.0-blocker`

`policyshield doctor` проверяет здоровье, но сервер **не падает** при невалидном конфиге на старте (`TELEGRAM_TOKEN` задан но невалидный, порт занят, несуществующий путь к правилам). Должен быть fail-fast.

- **Усилия**: Маленькие (~50 строк, startup checks)
- **Ценность**: 🔥🔥🔥 — экономит часы отладки, невалидный конфиг должен быть fatal

### Fail-Open / Fail-Closed Strategy 🔴 `v1.0-blocker`

Если сам PolicyShield упал (OOM, uncaught exception в engine, timeout regex) — что происходит с tool call? **Сейчас — неопределённое поведение.** Для production это критический вопрос.

```yaml
server:
  on_error: block     # fail-closed (безопасно, но тулы встанут)
  # on_error: allow   # fail-open (тулы работают, но без защиты)
```

Должен быть:
- конфигурируемый `on_error` с дефолтом `block` (безопасный)
- try/except обертка вокруг `engine.check()` в HTTP handler
- метрика `policyshield_engine_errors_total` для мониторинга

- **Усилия**: Маленькие (~30 строк, try/except + config option)
- **Ценность**: 🔥🔥🔥 — без этого поведение при сбое не определено

### Engine Check Timeout 🔴 `v1.0-blocker`

Если `engine.check()` зависает (катастрофический backtracking в regex, бесконечный loop) — агент ждёт вечно. Нет таймаута.

```yaml
server:
  check_timeout: 5s   # максимальное время на один check
```

- **Усилия**: Маленькие (~20 строк, `asyncio.wait_for` в handler)
- **Ценность**: 🔥🔥🔥 — fail-fast при зависании, без этого один regex кладёт весь сервер

### Atomic Hot-Reload 🔴

Текущий `reload_rules()` не атомарный: если новые правила невалидны, старые уже сброшены → **окно без защиты**. Должен быть "load new → validate → swap".

```python
# Сейчас:
def reload_rules(self):
    self.rules = parse(self.rules_path)  # если упадёт → rules = None

# Нужно:
def reload_rules(self):
    new_rules = parse(self.rules_path)  # если упадёт → старые остаются
    validate(new_rules)                  # проверить до свапа
    self.rules = new_rules               # атомарный swap
```

- **Усилия**: Маленькие (~20 строк, переставить строчки)
- **Ценность**: 🔥🔥🔥 — без этого hot-reload может убить защиту

### Startup Self-Test / Smoke Check 🔴

При старте сервер загружает правила и запускается. Но не проверяет их **работоспособность**. Невалидный regex (`[unterminated`) может пройти `validate` но упасть при первом реальном запросе.

```bash
# При старте сервера автоматически:
# 1. Скомпилировать все regex паттерни
# 2. Прогнать каждое правило на синтетическом input
# 3. Проверить все detectors
# При ошибке → не стартовать, показать что сломано
```

- **Усилия**: Маленькие (~50 строк, dry-run при startup)
- **Ценность**: 🔥🔥🔥 — ловит broken rules **до** production, не после

### Startup Python Version Validation 🔴 `v1.0-blocker`

README указывает `Python 3.10+`, но **нет runtime проверки**. На Python 3.8/3.9 пользователь получает непонятный `SyntaxError` на `X | Y` type unions или `match/case`. Должен быть **понятный fatal error на старте**, а не Python traceback.

```python
# __init__.py или cli entry point
import sys
if sys.version_info < (3, 10):
    sys.exit("PolicyShield requires Python 3.10+. Current: {}.{}".format(*sys.version_info[:2]))
```

- **Усилия**: 3 строки
- **Ценность**: 🔥🔥🔥 — без этого пользователь видит непонятный трейсбек и думает что проект сломан

### Structured Logging (JSON) 🔴

Сейчас `logger.warning()` пишет plaintext. Для Datadog/ELK/CloudWatch нужен JSON formatter. `structlog` или stdlib `logging.config`.

```json
{"level":"warning","event":"pii_detected","tool":"send_email","pii_types":["EMAIL"],"ts":"2026-02-18T00:30:00Z"}
```

- **Усилия**: Маленькие (~30 строк конфигурации)
- **Ценность**: 🔥🔥 — production observability
