#398 · Radar: ожидание без неопределённости
Открыть интерактивный макет ↗ · Дизайн и локальная симуляция. Source implementation, DEV acceptance и Owner UAT остаются NOT_TESTED. Внешние результаты проверки макета приведены ниже.
Основа: current68 568fe0443064a54e3dfe198f20a706258245cbb9; соответствие accepted DEV68 2c33ce49d5b8ad704bf78524b8149c94e4d3e62e — утверждение trusted parent. Все 12 source-proof SHA256 и два pinned Style Guide файла сверены локально. Предыдущий preflight использован только как история. Атрибуция: текущий Owner-requested gpt-6-astra / HIGH дизайн-вызов; без иных моделей, providers или subagents; квалификация модели не заявляется.
Композиция и запуск
Открыть index.html локально; все зависимости рядом. product.html — только рамка продукта; styles.css, scenarios.js, prototype.js, gallery.js, assets/onest.ttf, assets/go-badminton.svg. Авторский состав отражён в artifact-manifest.json; публикационный whitelist с QA-доказательствами находится в design-manifest.json. Публиковать только этот whitelist, никогда родительский каталог. capture-plan.json содержит 10 адресов и размеры.
Под текущим toolbar один статус с постоянным местом для двух компактных строк (на мобильном видна одна). Скелет статичен; названия, цифры, расстояния и пины до результата отсутствуют. Обычный каталог отдельно, всегда доступен; «К обычному списку» переводит фокус и прокручивает, не выключая Radar. Резерв одинаков для всех состояний; при увеличении шрифта блок может расти без обрезания. Синтетические профили отмечены вне рамки. Дальние маршруты передают выбор в панель оператора, экран назначения не переизобретён.
Точные тексты → реальные состояния → действия
| Состояние / заголовок | Условие и действие в приложении |
|---|---|
| off · «Radar выключен» consent · «Подтвердите включение Radar» | permission() открывает единственный ProfileDialog в RadarSessionProvider: текущее disclosure из config, RadarDurationChoice, явное согласие. В макете только внешняя симуляция; ОС отдельно. cancel() до dispatch. |
| locating · «Определяем местоположение…» activating · «Включаем Radar…» | RadarView.phase; switch OFF до подтверждённой активации. Отмена только до mutationDispatched через cancel(). После отправки POST отмены/повтора включения нет. |
| loading · «Загружаем игроков рядом…» ready · «Игроки рядом» | Подтверждённая активация + актуальный UnifiedDirectoryController. busy относится только к запросу nearby; ready требует renderable(filters, activationKey), snapshot и допустимые rows. RadarView.loading не источник результата. |
| denied · «Нет доступа к местоположению» timeout · «Не удалось дождаться геопозиции» | code 1 → denied. Timeout только реальный code 3; сейчас все остальные коды → location_error / «Не удалось определить местоположение». Повтор → permission(), без автоматического согласия. |
| networkerror · «Не удалось загрузить игроков рядом» empty · «Сейчас рядом никого нет» | Ошибка текущего semantic read ≠ пусто. Empty только успешный свежий авторизованный snapshot текущих id:version/фильтров с нулём Radar rows. «Повторить» использует текущий query gate. |
| unknown · «Проверьте статус Radar» stopping · «Выключаем Radar…» | Неизвестный POST → reconcile(), без автоматического POST retry. Pending stop сохраняет confirmedOn, скрывает private rows, OFF лишь после подтверждения. stop_unknown предлагает сверку. |
| stale · «Не удалось обновить игроков рядом» expired · «Radar завершён» | Предыдущие rows только тот же actor/activationKey/filter, renderable и validUntil. При TTL, revoke, block, account switch удалить private rows немедленно. Soft refresh не превращать в busy; обычный каталог не очищать. |
Узкий implementation handoff
RadarControls.tsx: заменить полное скрытие directory-compact адресным feedback; subtitle DirectoryRadarSwitch брать из phase при pending/error. PlayersDirectoryPage.tsx: связать scoped feedback с DirectoryView.busy/snapshot/notice/stale и existing activationKey, renderable/validUntil; не использовать глобальное раскрытие всех notices. Оставить обычную пагинацию и объединение/dedupe, фильтры, actor/session fences, TTL и consent/duration/privacy/profile visibility.
Текущие gaps: radar-controller.ts теряет code 3 — сохранить тип причины в существующем location_error, иначе generic copy. Для read-error нужен узкий typed presentation mapping существующего отказа, не разбор текстовой notice. apiFetch не имеет default deadline: долгий read остаётся loading с выходом в каталог/подтверждённым stop; сетевой timeout не выдумывать. POST /v1/players/query сохраняет cooldown 30s, 429 Retry-After и nextRefreshAt; retry доступен лишь по текущему gate. Никаких дополнительных polling, таймеров, transport/service architecture.
STYLE_GUIDE_PARITY и проверка
Повторены Onest, frozen palette, SurfacePanel/StatusCard/Notice/Skeleton/ListRow, радиусы 12/18, текущие RootNav/brand/icons, компактный switch. Make PlayersPage даёт семью Radar notices; current68 сохраняет toolbar/header и #526 GamesViewAction: белая pill, lime tile, точный purple FoldedMapGlyph. На мобильном Свайп и карта внутри одного существующего overlay owner; max-height reserve и safe-area. STYLE_GUIDE_GAP: компактная композиция многошагового feedback. OWNER_VISUAL_OVERRIDE: статичное ожидание вместо scan/spin и скрытых feedback, прямое поручение Owner #398. Whole Players redesign отсутствует.
Локально: JS syntax, source hashes, ссылки/allowlist, state invariants. Внешний оператор: capture-plan, keyboard/focus ≥44px, polite aria-live вне aria-busy query region, reduced motion, 200% text, отсутствие горизонтального overflow и скачка catalog Y, доступность каталога при зависшем read. Адресно проверить consent→ОС→POST→read, denied/code3, actual empty/error, unknown reconcile, cooldown, stale TTL/revoke/account switch, pending stop; принятые unchanged проверки переиспользовать. На завершение авторского вызова browser/independent QA и screenshots ещё не выполнялись. Исходники/DEV не изменены.
Внешняя проверка артефакта
09.11.2026 выполнены 38 проверок геометрии и 8 переходов в Chromium на 390/1280. Ошибок JavaScript и горизонтального переполнения нет. Положение обычного каталога одинаково во всех 19 состояниях при каждом размере. Сняты 10 продуктовых PNG и один контекстный кадр. Исправлены CSS-минимумы двух кнопок иллюстративного header до 44px и резерв высоты Radar switch до 64px для длинного статуса. Независимая проверка выявила выход кнопок статуса за левый край при реальном увеличении текста до 200%; добавлены естественный перенос кнопок и растущая строка действий. Размер шрифта сохранён. Изменения относятся к артефакту. Результаты оператора · Независимый отчёт.
Интеграция затрагивает apps/web/src/components/consumer/radar/RadarControls.tsx, apps/web/src/components/consumer/pages/PlayersDirectoryPage.tsx и адресные стили apps/web/src/styles/radar.css/players.css. Типизированная причина code 3 при необходимости относится к apps/web/src/lib/radar-controller.ts и существующему radar-types.ts. Окружающие mock header, профили, каталог и карта служат контекстом макета. В приложении использовать текущие ConsumerShell, реальные карточки и GamesViewAction. Прямое поручение Owner охватывает дизайн и дальнейшую реализацию loader; дополнительный approval gate не вводится.