Files
shturman/docs/domains/i-navigation.md
kk0t9 2eaa561892 docs(domain I): v2 — навигация (offline OSM/роутинг/turn-by-turn), после adversarial-ревью (24 находки) + кросс-док
Последний домен. Offline-first навигация: OSM-карты (MapLibre GL Native),
оффлайн-роутинг (Valhalla), turn-by-turn, поиск/POI, связка с ассистентом.
Не safety/ADAS (#1). Фаза v4. Многоагентный adversarial-ревью: 28 находок,
24 подтверждены (default-refute), все применены.

Ключевое из ревью:
- stale нав-промпт под звонком (проигрался бы ПОСЛЕ поворота) → продюсер I снимает заявку по окну релевантности; H остаётся контент-агностичным.
- Нет секции отказ-путей → добавлена (no-route/geocoder-fail/битые-тайлы/ENOSPC, симметрично J/H/L).
- Маршрут через границу регионов / назначение в нескачанном регионе → детект покрытия + предложение докачать / маршрут до края.
- reroute-шторм в urban-canyon → детект схода с гистерезисом + HDOP/FixQuality-гейт; dead_reckoning трактуется как нет-фикса.
- Якорь RDS-TMC G §7 → H §7 (FM-радио живёт в медиа-тракте H, не в сетевом G).
- MapLibre Native = BSD-2-Clause (не BSD-3); ODbL-атрибуция (#12); единицы км/мили из локали (data-model §4, #10).
- Интент «домой» разведён: D §6 = домашний экран (v1), навигация-домой = Nav-интент (v4).
- Добавлены секции Зависимости и last-parked (K §2/L §5 ждали); Boot-Stage 2; speed-limit — пассивный бейдж, не предупреждение (#1).

Кросс-док: d-assistant §6 («домой» = домашний экран; навигация-домой → Nav v4).
(architecture §3/§6 уже содержат Nav; a-base apps-storage уже покрывает нав-PII.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 14:23:34 +03:00

233 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Домен I — Навигация
> Офлайн-навигация: карты OSM, оффлайн-роутинг, turn-by-turn, поиск/POI, связка с ассистентом.
> **Offline-first** (#3 — карта не теряется в тоннеле/глуши). First-party ап на SDK; богатая
> карта-поверхность. Не safety/ADAS (#1). Поздний слой — **v4** (vision-лестница: нав+образ+ретрофит).
Статус: **v2 (на ревью).** v2 — после adversarial-ревью (24 находки).
Связано с: [tech-stack.md](../tech-stack.md) (Карты: MapLibre + Valhalla/OSRM) · [architecture.md](../architecture.md) (§3 Nav — first-party, §6 boot Stage 2) · [ipc.md](../contracts/ipc.md) (`Nav`, `Location`) · [data-model.md](../contracts/data-model.md) (§4 единицы — конвертация на презентации) · домены **K** (§2 `Location`/FixQuality/zero-clamp, §3 руль, §4 IMU-опц., §10 DR), **H** (§3 роль `nav_guidance`, **§7 RDS-TMC задел**), **D** (§6 интенты, §2 TTS), **C** (§4 слоты, §6 тема, §7 distraction), **E** (§2 OBD-скорость v2), **G** (§2 онлайн/State/metered), **A** (§3 fscrypt/хранилище, §10 износ, §12 reset), **L** (§5/§7 обновление карт/синк), **B** (§4/§7 lifecycle) · [security-privacy.md](../contracts/security-privacy.md) (§3 `location`, §5 while-in-use, §7 приватность) · [principles.md](../principles.md) (#1,#3,#6,#8,#10,#11,#12,#13)
---
## 1. Назначение и границы
- **Что делает:** офлайн-карты + оффлайн-роутинг + turn-by-turn + поиск/POI + ввод назначения; связка с D и H.
- **Offline-first (#3) — определяющее:** карта/роутинг/ведение работают **без сети**; онлайн (трафик/поиск) — опц. улучшение.
- **Границы:**
- **Не safety/ADAS (#1):** ведёт, не управляет; никаких lane-keeping/ACC/регуляторных claim'ов.
- **Speed-limit display (OSM `maxspeed`):** если в скоупе — **строго пассивный инфо-бейдж** текущего сегмента,
**БЕЗ детекта/звукового предупреждения о превышении** (это warning/ADAS-зона — красная линия #1); камеры
контроля скорости/red-light — вне скоупа. Данные `maxspeed` неравномерны → риск ложного claim'а (§18).
- **Не источник позиции:** GPS/положение — у **K** (`Location`); I — потребитель + map-matching.
- **Не источник trip-данных машины:** пробег/средний расход (trip-производные) — trip-плагин (E §2, plugin-sdk §8);
мгновенный расход — core E. I считает только свои **нав-метрики** (ETA/дистанция).
- **Provider-agnostic роутинг (#8):** движок сменный за единым интерфейсом.
- **Plane:** карта — **Wayland-поверхность** (GPU) в слоте Shell (C §4); control — D-Bus (`Nav`); голос — PipeWire (`nav_guidance` H §3).
- **Фаза:** v4 (вся; онлайн-улучшения — later).
## 2. Карты: данные, формат, хранилище, лицензия
- **Источник — OpenStreetMap.** 🟡 **Лицензия ODbL** (OSM-данные): обязательна **атрибуция** «© OpenStreetMap
contributors» (в UI карты + «о картах»); share-alike — на **производную базу** (не на наш MIT-код), данные-актив
отдельно от кода (#12). → §18.
- **Формат:** **векторные тайлы** (MapLibre) — **PMTiles** (один файл/регион, offline) или MBTiles; роутинг-граф —
тайлы движка (§4). Сборка: OSM-экстракт (регион) → tilemaker/planetiler + Valhalla-тайлы.
- **Регионы по запросу:** качаем только нужные (РФ целиком — единицы–десятки ГБ), не весь планет.
- **Версионная когерентность:** **map-snapshot региона = когерентная пара** рендер-тайлов (PMTiles) + роутинг-тайлов
**одной OSM-версии**; оба набора версионируются и **заменяются атомарно вместе** (запрет смешения версий → иначе
геометрический рассинхрон рендер↔роутинг; ср. version-coherence L §6/§7).
- **Хранилище:** карты — крупный **read-mostly** актив; **отдельный носитель/раздел или `/data`** (a-base §3; износ
минимален — чтение, a-base §10). 🟡 размещение (§18).
- **Обновление:** периодическое, через **канал L §5/§7** (контент, не прошивка A) или ручная загрузка по `Connectivity`
(G §2, metered/online-гейт). **Атомарная замена региона** (не рвать активный маршрут — §13).
## 3. Рендеринг карты
- **MapLibre GL Native** (**BSD-2-Clause**, #12; NB: MapLibre GL **JS** — BSD-3, это другой слой) — векторные тайлы,
GPU-рендер через **Mesa/Panfrost** (RK3588), Wayland-поверхность в слоте Shell (C §4, slot-протокол). Плавность (#11) — GPU.
- **Тема день/ночь** — map-style-токены, синхронно с темой C §6 (по времени → GPS-восход K).
- **Виды:** 2D/3D, north-up/heading-up; маркер позиции (snap-to-road §7); оверлей маршрута + next-maneuver.
- На стоянке/без маршрута — свободный просмотр; в движении — гейтится distraction (владелец **C §7**; детали — §10).
## 4. Роутинг (offline)
- **Движок 🟡:** **Valhalla** (рек. — MIT, тайловый, on-device, costing) vs **OSRM** (BSD, быстрый, тяжёлый
препроцессинг/RAM). Provider-agnostic (#8). → §18.
- **Что даёт:** маршрут A→B (+ промежуточные), **ETA/дистанция**, профиль, **rerouting** при сходе (§7), альтернативы.
- **Покрытие регионами (важно для региональной нарезки §2):** назначение/коридор **вне загруженных тайлов**
**детектируется**: при сети — предложение докачать регион (канал §2, metered/online-гейт G §2); офлайн/регион
недоступен — маршрут до **края покрытия** с явной пометкой «дальше карта не загружена», не тихий обрыв графа.
Геокодер (§6) для адреса вне загруженных регионов возвращает статус «вне загруженных карт», не пустой результат.
- **No-route-found** (недостижимо / точка вне графа / разрыв покрытия / отсечено costing) — **явный отказ-исход**
(§12), `NavigateTo``RouteNotFound`/`RouteActive=false` (§14), не зависание.
- **Дисциплина детекта схода (анти-reroute-шторм):** off-route объявляется только при **устойчивом** отклонении
> порога в течение N фиксов **И** при приемлемом `HDOP`/`FixQuality` (K §2; высокий HDOP / только `fix_2d` →
удерживать маршрут); при zero-clamp скорости (K §2) rerouting приморожен.
- **ETA — оценка** (см. §8): offline без трафика; остаток — от текущей позиции по маршруту (не от фикс-старта).
## 5. Ведение (turn-by-turn) и аудио
- **Turn-by-turn:** next-maneuver-карточка (поворот/дистанция/полоса-опц.) + голосовые промпты.
- **Голос — роль `nav_guidance`** (H §3): дакает медиа; под ассистента/звонок — очередь (H §3). Синтез — **TTS D**.
Тайминг — по **скорости** (K `Location.Speed`, доступна с **v1**; Nav потребляет в v4; уточнение OBD-скоростью E §2/v2)
и дистанции до манёвра.
- **Stale-промпт (не durable-очередь):** у каждого `nav_guidance`-промпта **окно релевантности** (дистанция/время до
манёвра). Пока длится cork звонка / подавление под ассистента — продюсер **I сам снимает заявку**, если манёвр
пройден или окно истекло (H остаётся контент-агностичным, H §2 — снятие устаревшего на стороне I, не арбитра H). По
освобождению фокуса I **переоценивает** текущий манёвр по реальной позиции (map-matching §7) и эмитит свежий промпт,
а не доигрывает протухший.
- **Map-matching:** snap позиции к дорожной сети для отображения и детекта схода/манёвра.
- **Деградация (#4):** нет голоса/занят (H) → карточка остаётся; нет фикса (§7) → ведение приостановлено с пометкой.
## 6. Поиск, POI, ввод назначения
- **Ввод:** (1) **голос** — интент D §6 (§9); (2) поиск (адрес/POI); (3) **избранное/дом/работа**; (4) тап/долгий тап.
- **Оффлайн-геокодер/поиск** из OSM-данных (адрес + POI); тяжёлый офлайн-индекс — на устройстве, фоном (#11); класс — §18.
Адрес **вне загруженных регионов** → честный статус «вне загруженных карт» (§4), не пустой «ничего не найдено».
Онлайн-геокодер/POI — опц. улучшение (#8).
- В движении ввод текста — блокируется distraction (C §7); приоритет голосу/пресетам (#6).
## 7. Позиционирование (потребитель K)
- **Основа — `Location` (K §2):** валидные `fix_2d/3d/augmented`; **`dead_reckoning` трактуем как ОТСУТСТВИЕ
доверенного фикса** (наравне с `no_fix`; K §2: не валиден для Nav) → не используем для snap/детекта схода, идём путём
GPS-loss. Zero-clamp скорости у нуля (K §2) уважаем.
- **GPS-loss при активном маршруте (тоннель):** **route-based экстраполяция** вдоль маршрута по последней скорости
(модест, БЕЗ заявки точности, помечено «оценка»).
- **GPS-loss без маршрута (free-drive):** route-опоры нет → маркер **замораживается** на последней валидной позиции с
пометкой «нет сигнала», НЕ уводится «живой» оценкой.
- **Ресинк по выходу:** первый(е) фикс(ы) после потери **не доверяем как off-route-триггеру** до стабилизации качества
(низкий HDOP, K §2) — окно подавления rerouting; визуальный скачок маркера **сглаживаем**; при устойчивом расхождении
(пропущенный съезд в слепой зоне) — честный reroute, не «доведение» по устаревшей ветке.
- **Джиттер при валидном фиксе (urban-canyon, multipath):** позиция дрожит, но zero-clamp (K §2) лечит только скорость
→ детект схода HDOP-гейтится (§4), не вызывает reroute-шторм.
- **Полноценный sensor-fusion DR (IMU+GPS) — открытый вопрос K §10** (IMU опц./часто нет, K §4) — I НЕ закладывается.
- **Cold-start окно (K §2):** после `battery-cutoff` поездка может стартовать без фикса — ведение деградирует штатно (#3,#4), не висит.
## 8. Трафик *(онлайн/задел)*
- **Offline — нет live-трафика;** роутинг работает без него. **ETA offline — оценка по costing БЕЗ трафика/заторов**,
в UI помечается «оценка» (как позиция при no-fix §7), чтобы не давать ложной уверенности (#1).
- **TMC через RDS** (FM-радио, **H §7 задел** — RDS живёт в медиа-тракте H, FM-тюнера ещё нет, hardware §4/H §15) — later.
- **Онлайн-трафик** — опц. провайдер (#8), при `Connectivity` online (G §2); кормит costing (§4). Later.
## 9. Ассистент-интеграция (D)
- **Интенты:** «проложи до X», «поехали домой/на работу», «сколько ехать» (ETA), «перестрой», «отмени». **Быстрый
локальный матч фразы** (без LLM, латентно-критично) — у ассистента **D §6**; **резолв места** (дом/работа/избранное
§6, геокодер) **и исполнение****Nav-IntentHandler** (`assistant_intents`). Nav-обработчик появляется с **v4**
(вместе с избранным §11); до Nav (v1v3) нав-фразы → graceful «навигация недоступна» (снимает кажущийся конфликт с
D §6, где «домой» = домашний экран, не навигация).
- **Nav-context → ассистент:** ETA/следующий манёвр/остаток — в контекст (как vehicle-context D §4), по запросу.
- Промпты ведения и ассистент делят аудио по лестнице H §3 (`assistant_tts` > `nav_guidance`).
## 10. Driver-distraction (#6, владелец — C)
- В движении: упрощённая карта, **блок ввода текста/сложного**; приоритет голосу/пресетам. Руль-ввод — uinput (K §3);
часть действий гейтится distraction (**C §7** / K §3). Правило едино через Shell/SDK.
## 11. Приватность (дом/работа/избранное/история — PII)
- **Места и история — персональные данные локации (152-ФЗ, высокочувствительно):** дом/работа/избранное/недавние
назначения/**last-parked** (+ опц. трек, K §2 «Nav — v4»). **On-device, fscrypt-поддерево** (a-base §3, в
`/data/apps/<nav>/`); **factory-reset-wipe** (a-base §12); доступ — audit-log; `location` while-in-use (K §2, sec-priv §5).
- **Отдельный чувствительный store, исключённый из дефолтного Settings-синка L:** нав-избранное/дом/работа синкаются
**только по явному отзываемому consent** (как память D §7 / контакты G §5 / точная локация L §5) — не автоматически
с обычными настройками; **не в LLM-промпт** без согласия (sec-priv §7).
## 12. Отказ-пути (fail-safe, симметрично J §3/§4 · H §5 · L §7)
- **No-route-found** (§4) → видимая неблокирующая индикация «маршрут не найден», ведение не стартует, `RouteActive=false`; не зависание/пустой экран.
- **Сбой/пустой геокодер** (битый индекс / ничего не найдено) → «ничего не найдено» + фолбэк (тап по карте §6), не крэш поиска.
- **Битые/отсутствующие тайлы региона** (рендер/роутинг) → видимая пометка деградации, не белый экран и не крэш GPU-рендера.
- **ENOSPC при загрузке региона** (единицы–десятки ГБ, §2) → отказ ДО записи с видимой индикацией, без частичного
тайл-набора (проверка места — как L §7); недоступный источник карт → отложить (#3).
## 13. Lifecycle (шов с B)
- **Boot-Stage:** Nav-сервис — **Stage 2** (фоном; architecture §6 перечисляет Nav в Stage 2); карта-поверхность — в слот Shell после Stage 1.
- **Resume маршрута:** на `ShutdownImminent` (B §4) durable-save состояния. **Валидность resume:** (а) тайлы региона
назначения присутствуют той же версии (иначе не пересобирать молча — §2/§18, предложить скачать/отменить); (б) до
первого фикса (cold-start §7) — показать сохранённое назначение, но НЕ вести по устаревшему прогрессу (§5); (в) после
фикса — если позиция вне коридора, штатный сход → rerouting (§4/§7), не слепое продолжение.
- **Прибытие:** на `Arrived` (§14) — завершение ведения у точки; **захват last-parked** (позиция, fscrypt §11); парковочный «последние метры» — опц./later.
- **Не держит wake** (B §7, #5); ведение — в `running`/`accessory`.
- **Память (a-base §8):** рендер-буферы/тайл-кэш — потребитель; throttleable под UI-приоритет (#11).
## 14. IPC / интерфейсы
- **`ru.shturman.Nav`** (ipc §4, домен I): `NavigateTo(dest)` (→ `RouteNotFound` если недостижимо/вне графа),
`CancelRoute()`/`RerouteNow()`, `Search(query) → [results]` (статус «вне загруженных карт» §4), `ListFavorites()`;
сигналы `RouteChanged`/`ManeuverChanged(next)`/`Arrived`; properties `RouteActive` (false при no-route), `Eta`
(**оценка без трафика** §8), `DistanceRemaining`, `NextManeuver`.
- **Потребляет:** `Location` (K — позиция/скорость/FixQuality), `Connectivity` (G — онлайн опц.), `Power` (B — тайминг),
Settings (**тема**; нав-избранное — отдельный fscrypt-store §11, не Settings-namespace), TTS (D). **Гейтинг:**
`location` (sec-priv §3/§5, while-in-use); `network` (онлайн).
- **UI:** карта-поверхность + next-maneuver-карточка + поиск — слоты Shell (C §4); голос — PipeWire (`nav_guidance`).
## 15. Зависимости
- **Вниз:** tech-stack (MapLibre/Valhalla), A (§3 хранилище/fscrypt, §10 износ, §12 reset), hardware (GPU Mesa/Panfrost RK3588), data-model (§4 единицы).
- **Вбок:** **K** (§2 `Location`/FixQuality/zero-clamp/DR, §3 руль, §10 DR-открыт), **H** (§3 роль `nav_guidance`/ducking, §7 RDS-TMC),
**D** (§6 интенты, §2 TTS), **C** (§4 слоты, §6 тема, §7 distraction), **E** (§2 OBD-скорость v2), **G** (§2 онлайн/metered),
**B** (§4/§7 lifecycle), **L** (§5/§7 обновление карт/синк избранного с consent), security-privacy (§3/§5/§7 локация-приватность).
- **Вверх:** nav-context (ETA/манёвр/остаток) → D (как vehicle-context); карта-поверхность → Shell (C); last-parked → потребитель L (§5 «где машина»); расширяемо плагинами.
## 16. Dev-симулятор (#13)
- **Fake-GPS-трек** (NMEA-реплей K §2: маршрут/тоннель-no-fix/дрейф/cold-start), **тест-регион** (малый PMTiles +
Valhalla-тайлы), **мок-маршрут** (rerouting/maneuver-тайминг/`nav_guidance`-ducking H §12). Краевые сценарии:
**дрейф в каньоне при валидном фиксе → НЕ reroute** (анти-pumping §7), назначение вне региона (§4), no-route,
stale-промпт под звонком (§5). Поверх fake-GPS/мок-сети. → dev-environment.
## 17. Функции
| функция | MVP/later | зависит от | фаза |
|---------|-----------|------------|------|
| Офлайн-карты (OSM/ODbL, PMTiles, version-coherent) | **MVP** | tech-stack, хранилище | v4 |
| Рендер карты (MapLibre GL Native BSD-2, GPU, поверхность) | **MVP** | C §4, Mesa/Panfrost | v4 |
| Офлайн-роутинг (Valhalla 🟡, ETA-оценка, rerouting+гистерезис) | **MVP** | движок | v4 |
| Покрытие/детект региона (вне загруженных тайлов) | **MVP** | §2, G §2 | v4 |
| Turn-by-turn + промпты (`nav_guidance`, stale-drop) | **MVP** | H §3, D | v4 |
| Map-matching + позиционирование (GPS-loss/free-drive/ресинк) | **MVP** | K §2 | v4 |
| Ввод назначения (голос/поиск/избранное/тап) | **MVP** | D §6, геокодер | v4 |
| Офлайн-геокодер/POI (статус «вне региона») | **MVP** | OSM-данные | v4 |
| Избранное/дом/работа/last-parked (fscrypt, consent-синк) | **MVP** | a-base §3, sec-priv, L | v4 |
| Отказ-пути (no-route/geocoder/битые-тайлы/ENOSPC) | **MVP** | §4/§6/§2, L §7 | v4 |
| Resume маршрута (валидность по тайлам/фиксу) | **MVP** | Settings, B, §2 | v4 |
| Speed-limit инфо-бейдж (пассивный, не предупреждение) | later | OSM `maxspeed` | v4 |
| Обновление карт (канал L / ручное, атомарно) | later | L §5/§7, G | v4 |
| Трафик: TMC/RDS (задел) | later | **H §7** | later |
| Трафик/поиск онлайн (опц. #8) | later | G §2 | later |
| Sensor-fusion DR в тоннелях (IMU+GPS) | later | K §10 (открыто) | later |
## 18. Открытые вопросы
- 🟡 **ODbL** (OSM): атрибуция обязательна; share-alike на производную базу — оформить (атрибуция в UI). → #12/legal.
- 🟡 **Движок роутинга** Valhalla (рек.) vs OSRM — выбрать (вес/RAM/инкрементальность on-device). → tech-stack.
- 🟡 **Размещение карт-данных** (отдельный раздел/носитель vs `/data`; износ a-base §10; размер региона). → a-base/hardware.
- ◻️ **Speed-limit display** из OSM `maxspeed` — покрытие/достоверность неравномерны (риск ложного claim'а #1); включать ли пассивный бейдж и как позиционировать. → #1/legal.
- ◻️ **Визуальные нав-единицы** (км/м vs mi/ft в карточке/ETA/`DistanceRemaining`) + формат/порядок адреса — из локали Settings (C), конвертация на презентации (data-model §4); RU-first метрический дефолт (#10).
- ◻️ **Канал обновления карт** (через L-контент vs отдельный; атомарная замена региона + version-coherence пары; дельта). → L.
- ◻️ **Sensor-fusion DR в тоннелях** (IMU+GPS) — нужен ли, где живёт (K-fusion/gpsd/Nav). → K §10 (парный).
- ◻️ **Офлайн-геокодер** (Pelias/Nominatim-класс vs упрощённый индекс) — вес/качество на RK3588. → реализация.
- ◻️ **3D/buildings/landmark-guidance** — объём данных vs ценность. → later.
- ◻️ **Голосовые языки промптов** (RU-first #10; озвучка названий улиц — TTS D). → D.
---
## Журнал решений (домен I)
| Решение | Выбор | Дата |
|---------|-------|------|
| Позиционирование | offline-first нав; онлайн — опц. улучшение; не safety/ADAS (#1; speed-limit — пассивный бейдж, не предупреждение); v4 | 2026-06-23 |
| Карты | OSM (ODbL — атрибуция); PMTiles + Valhalla-тайлы **когерентной парой**, атомарная замена региона | 2026-06-23 |
| Рендер | MapLibre GL Native (**BSD-2-Clause**), GPU Mesa/Panfrost, Wayland-поверхность слот C | 2026-06-23 |
| Роутинг | offline, provider-agnostic (#8); Valhalla рек. (🟡); region-coverage-детект; no-route-found; reroute с гистерезисом | 2026-06-23 |
| Ведение | turn-by-turn; `nav_guidance` (H §3) → TTS D; **stale-промпт снимает продюсер I** (H контент-агностичен) | 2026-06-23 |
| Позиция | потребитель K `Location`; `dead_reckoning`=нет-фикса; route-based экстраполяция (с маршрутом) / freeze (free-drive); ресинк-дисциплина | 2026-06-23 |
| Приватность | дом/работа/избранное/история/last-parked — PII: fscrypt, отдельный store (не Settings-синк), consent-only, reset-wipe | 2026-06-23 |
| Ассистент | матч фразы — D §6; резолв+исполнение — Nav-handler (v4); до Nav — graceful «недоступна» | 2026-06-23 |
| Lifecycle | Stage 2 (architecture §6); resume по валидности тайлов/фикса; last-parked на Arrived; не держит wake | 2026-06-23 |
| Отказ-пути | no-route/geocoder-fail/битые-тайлы/ENOSPC — видимая индикация, не крэш (симметрично J/H/L) | 2026-06-23 |