34 KiB
CODE-REVIEW-582-r2
Issue: #582 · этап: code · заход r2 ·
блокирующих циклов израсходовано 2 из 4 · материал: 226f323073afd79a1eef9e2e5979684c9ae628f7
(origin/dev merge-base = 4208ca81fe3516200a5bd80c1221597492d8fa7b, дерево материала =
dca5f4042deec2196302371f3ba4ffd6ab59c242).
Скоуп
Красный r1 (CODE-REVIEW-582-r1.md, материал 0a6592f4) нашёл два High: AC4 (перф-свидетель
smoke_daycycle_raster красный 8/8, plan-svg продавливался в отдельный composited-слой из-за
"Overlaps other composited content") и AC6 (все 4 day-cycle golden-сцены different, ~3.3–3.5%
пикселей). Между r1 и r2 ветку дважды двигали: «Повторный хендофф» переписал сам механизм
контура, затем ребейз на dev (конфликт с #581, материал стал 2a8b5f1d), затем красный
Validate с мутантами на 2a8b5f1d (ревью не запускалось, «код никто не читал» — не цикл), и
наконец стабилизация шумного raster-свидетеля коммитом 226f3230 — это и есть материал r2.
SHA r1 (0a6592f4112f) и его дерево (9f4d95168fdd) не резолвятся в этом чекауте —
git cat-file -t возвращает «could not get object info», в git ls-remote origin его тоже нет.
Это ожидаемо по PROCESS.md §2.10 (ребейз осиротил коммит, находкой не считается) — данные о r1
взяты не из живого диффа, а из текста docs/reviews/CODE-REVIEW-582-r1.md (коммит 728c564e,
который остался в истории, так как он был закоммичен уже после материала r1) плюс из текущего
кода. Раз architecture между r1 и r2 поменялась содержательно (не «одна строка в фикстуре», а
новый conditional-механизм переключения слоя), разбор ниже — полный по AC1–AC7, а не только по
находкам r1 (§2.9: «дельта не локальна» — сменился сам механизм, которым AC1–AC6 доказываются).
Материал и что сделано в коде (дельта r1→r2)
src/houseplan-card.ts: новое поле_safeDayCycleOutlineи метод_activateSafeDayCycleOutline()(строка ~2068), вызываемый из трёх точек входа жеста — двухпальцевый pinch (_pinchStart+_pointers.size>=2), обнаружение_panLock==='pan'(мышь и палец через общий pointer-путь) и multitouch-старт pinch. При активации кладёт классhp-safe-daycycle-outlineна_stageElнапрямую (минуя цикл Lit — сохраняет compositor lifecycle #579) и персистентно на весь срок жизни инстанса черезrender().- Новый
<svg class="hp-paper-outline-svg">теперь рендерится ВСЕГДА при активном day-cycle (не только после жеста), ноvisibility: hiddenдо появления.hp-safe-daycycle-outline— то есть до первого движения камеры карточка сохраняет байт-в-байт прежнюю (r1-довоенную, #532) однослойную композицию:.hp-papergдержитfilter+will-change:filterнапрямую, а скрытый сосед не создаёт собственного promoted-слоя (у него нет фильтра до активации). - После активации:
.hp-papergполучаетfilter:none; will-change:auto(перестаёт быть большим координатным слоем), фильтр иwill-change:filterпереезжают на.hp-paper-outline-svg(stage-размерный корневой<svg>), а.plan-svgполучает явныйwill-change: transform— это и есть исправление находки №1 r1: явная причина промоушена не даёт Chromium придумать implicit"Overlaps other composited content"для всего плана. src/render/paper-scene.ts— новый общийrenderPaperShapes()(вынесен из r1, без изменений сигнатуры), используется вhouseplan-card.tsиspace-render.ts.src/space-render.ts/src/space-card.ts— не получилиhp-paper-outline-svg/hp-safe-daycycle-outlineвообще (в r1 они были, симметрично основной карточке). Это разбирается как находка №1 ниже.demo/smoke_daycycle_layer_budget.mjs(новый в r1, здесь без изменений сигнатуры проверок) — свидетель AC1–AC3, теперь дополнительно проверяет, что до жеста контурvisibility:hidden, а после —.plan-svgполучаетwill-change:transformявным слоем.demo/smoke_daycycle_raster.mjs— переписан в r2 (комментарий issue «Исправление CI-свидетеля»): три чередующиеся замкнутые static/day-cycle пары, медиана парных отношений вместо одиночного замера — устраняет ложный красный на шумном раннере, порог 2.0 не менялся.scripts/mutation-registry.mjs— новый мутантdaycycle-outline-promoted-on-inner-paper(возвращаетwill-change:filterна.hp-papergв safe-режиме), старыйdaycycle-outline-not-promotedадаптирован (снимаетwill-change:filterс общего блока).- Документация (
ARCHITECTURE.md,SUN.md,TOUCH-SUPPORT.md,TESTING.md) переписана под новый двухфазный механизм и прямо признаёт, что статическая карточка в фолбэк не входит («the non-interactive static space card keeps its historical inner outline because it has no camera gesture (#582)» —docs/SUN.md). - Changelog (
docs/CHANGELOG.md/.ru.md) не менялся в этой дельте — запись сделана ещё вa68e4c6b(User-Visible: yes) и по-прежнему точно описывает финальное поведение основной карточки (перепроверено golden/smoke-прогонами ниже).
Как проверялось
Дешёвые гейты подтверждены зелёным Validate на точном материале
(https://github.com/Matysh/houseplan-card/actions/runs/34954286064) — предполёт, «Фронтенд:
типы/юниты/бандл», мутанты по диффу: success. Тяжёлые джобы в этом прогоне не гейтили пуш
(обычный push, не Release:/PR/full=true) — именно их r1 не хватило, поэтому здесь они
прогнаны мной лично.
| Гейт | Результат | Почему прогнан/не прогнан |
|---|---|---|
npm run bundle:sync (typecheck+build+rollup+bundle-tree) |
зелёный | нужен как основа для браузерных смоков; дёшево |
npm run bundle:budget |
зелёный, 291058 Б / потолок 291700±2000, запас 10008 Б (предупреждение о низком общем запасе — преэкзистентный долг #367/#474, не находка этой задачи) | обязателен после билда |
node scripts/check-docs.mjs |
зелёный, 7 файлов, 12 внешних ссылок | diff трогает src/** — обязателен всегда |
node scripts/process-gate.mjs --issues |
зелёный, 5 коммитов, 0 предупреждений | офлайн-проверка трейлеров/веток |
node demo/smoke_daycycle_layer_budget.mjs (AC1/AC2/AC3) |
зелёный, все 12 заявленных инвариантов true, 12/12 presented-кадров nearWhiteRatio:0 |
прямое совпадение — это и есть свидетель находок r1 |
node demo/smoke_daycycle_raster.mjs (AC4) |
зелёный, медиана парных отношений 1.55 (1.39/1.55/1.69) при потолке 2.0 | прямое совпадение — это и есть красный тест r1 |
npm run golden:verify (AC6, полная матрица 172 сцены) |
зелёный, 172/172 passed, все 4 day-cycle-{dawn,day,dusk,night}-dark в их числе |
diff меняет рендер; это и есть красная проверка r1 |
node demo/smoke_bg_color.mjs |
зелёный (все проверки, включая staticCardLayersStayOrdered) |
зарегистрированная связь renderPaperShapes |
node demo/smoke_live_pan_coverage.mjs |
зелёный | зарегистрированная связь renderPaperShapes |
node demo/smoke_pan_any_zoom.mjs |
зелёный (pinch/pan/kiosk-swipe инварианты) | прямое совпадение по _panLock, тот же код-путь, что получил новый вызов _activateSafeDayCycleOutline() |
node demo/smoke_isometric_contract.mjs |
зелёный | прямое совпадение — plan-svg класс стал безусловным, iso-обёртка контура повторяет isoFloorMatrixCss() |
node scripts/mutation-gate.mjs --id=daycycle-outline-promoted-on-inner-paper |
зелёный: чистый прогон ok, мутант поймал (1 из 1) | новый мутант AC2, дорогой гейт — обязателен по §2.7 (защита живёт в продуктовом коде) |
node scripts/mutation-gate.mjs --id=daycycle-outline-not-promoted |
зелёный: чистый прогон ok, мутант поймал (1 из 1) | существующий мутант r1, адаптирован под новый общий блок — перепроверен, что не сломался |
npx tsc --noEmit, npm test, npm run build (сверка копий) |
не гонял отдельно — подтверждены зелёным Validate на этом же SHA | дешёвые гейты, разрешено ссылаться на зелёный прогон (npm run build я всё равно выполнил как часть bundle:sync) |
python -m pytest tests_backend, HACS, Hassfest, geometry-parity |
не гонял | diff не трогает custom_components/**/*.py, манифесты, layout/marker.space/толщину стен — только SVG-рендер и стили |
node scripts/model-invariants.mjs |
не гонял | diff не меняет геометрическую модель/рёбра/толщину — только композитинг и разметку SVG |
Остальные 40+ «прямое совпадение» смоков из smoke-select.mjs (_mode, _modeTransitionBusy, _decorTool, _stageEl, _booting и т.п.) |
не гонял, кроме двух выше | это артефакт одной длинной изменённой JSX-подобной строки шаблона (<div class="stage ...">), в которой лежат десятки не изменившихся по смыслу свойств — слабая связь по PROCESS.md §8; прогнал два наиболее правдоподобных (smoke_pan_any_zoom, smoke_isometric_contract) точечно |
performance_smoke (полный, вне smoke_daycycle_raster) |
не гонял | AC4 уже проверен целевым, более дешёвым свидетелем; полный профиль не добавляет решающей информации на этом раунде |
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
High №1 — AC4 не держится: plan-svg продавлен в отдельный слой ("Overlaps other composited content"), raster ratio 2.16–2.50 против потолка 2.0 |
Явный will-change: transform на .plan-svg в safe-режиме убирает implicit-промоушен; контур скрыт до жеста, поэтому до-жестовый idle-рендер не платит вообще |
src/styles/plan.styles.ts:111-113 (.stage.daycycle.hp-safe-daycycle-outline .plan-svg { will-change: transform; }); мой прогон node demo/smoke_daycycle_raster.mjs — медиана 1.55 при потолке 2.0 |
High №2 — AC6 не проходит: все 4 day-cycle golden different, ~3.3–3.5% пикселей |
Контур не рендерится видимо (visibility:hidden) и не меняет .hp-paperg до первого жеста — golden снимаются на idle-карточке, которая теперь байт-в-байт прежняя композиция |
src/styles/plan.styles.ts:264-270 (.hp-paper-outline-svg{visibility:hidden} / .stage.hp-safe-daycycle-outline .hp-paper-outline-svg{visibility:visible}); мой прогон npm run golden:verify — 172/172 passed, все 4 day-cycle сцены включены |
Low №3 — комментарий про «the external day-cycle outline... is composited» на .hp-paperg, хотя фильтр туда больше не крепится |
Комментарий переписан под новую архитектуру | src/houseplan-card.ts, блок над renderPaperShapes(paperShapes): «One <g> keeps the visible sheet and filtered silhouette free of room seams.» — прочитано, соответствует коду |
Находки
Находка №1 (High) — Контракт п.6 не выполнен: houseplan-space-card не получил ограниченный экранными размерами механизм
Файлы: src/styles/plan.styles.ts:93-95 (правило .hp-static-stage.daycycle .hp-paperg),
src/space-render.ts, src/space-card.ts.
ТЗ (раздел «Контракт поведения», пункт 6, зелёное спек-ревью r1, без замечаний по этому пункту):
Тот же ограниченный экранными размерами механизм применяется в
houseplan-space-card, чтобы статическая карточка не сохраняла скрытый координатно-зависимый риск. Внешний вид и отсутствие интерактивных жестов этой карточки не меняются.
Текущий код .hp-static-stage.daycycle .hp-paperg не тронут дельтой r1→r2 вообще — та же
самая строка will-change: filter на координатно-большой .hp-paperg, что и до всей задачи
#582/#532. Ни hp-paper-outline-svg, ни hp-safe-daycycle-outline в space-render.ts/
space-card.ts не существует (в r1 они были — см. «Что проверено и корректно» r1: «hp-static-stage
получил аналогичное правило»; в r2 это убрано полностью).
Это не домысел ревьюера — это задокументированное и осознанное решение автора. Прямая цитата
из диффа docs/SUN.md (r2): «the non-interactive static space card keeps its historical inner
outline because it has no camera gesture (#582)»; из docs/ARCHITECTURE.md (r2): «the static
space card, which has no camera gesture, never enters the fallback». Тест
test/paper-scene-contract.test.mjs кодифицирует это же самое как требование:
assert.doesNotMatch(staticRender, /class="hp-paper-outline-svg"/) и
assert.doesNotMatch(staticCard, /\.hp-static-stage \.hp-paper-outline-svg/) — то есть попытка
исправить это в будущем сломает собственный тест задачи.
Почему это High, а не молчаливое наследование. Формулировка контракта явно и без условий:
«скрытый» риск назван так именно потому, что он не требует жеста — houseplan-space-card
рендерит тот же canonicalWallGeometry.paperD/paperRoomShapes(space.rooms) из того же
пространства с тем же cell_cm, то есть ровно тот же 4700×4200-координатный диапазон при
cell_cm:1, что и основная карточка (тот самый пример из тела issue). houseplan-space-card —
read-only карточка для встраивания (docs/ARCHITECTURE.md:1577), доступная в тех же дашбордах
HA Companion/kiosk, где и воспроизведён исходный дефект (docs/SCOPE.md: View — продукт для двух
из трёх персон, touch/View — release-blocking по docs/TOUCH-SUPPORT.md). Ни один из AC1–AC9
явно не тестирует компоситорную безопасность статической карточки (AC5 проверяет только
визуальную целостность контура и отсутствие лишней outline-сцены у статичного ФОНА/редакторов/
изометрии — это другой параметр, не про houseplan-space-card; AC7 проверяет только переиспользование
общего рендерера форм, не компоситинг) — то есть пробел в контракте образовался и не пойман
собственным тест-планом задачи.
Технически это не тот же самый регресс, что нашёл r1 (никакого overlap-промоушена plan-svg в
static-карточке нет, потому что там нет второго <svg>-соседа вообще) — риск ровно тот, ради
устранения которого пункт 6 был написан: постоянный координатно-большой will-change:filter.
Раз пункт 6 сформулирован как безусловное требование, принятое зелёным спек-ревью, и раз
реализация сознательно его не выполняет без вынесения вопроса владельцу (ТЗ §7.1 требует
эскалации продуктовых развилок, а не тихого решения в одиночку) — блокирует.
Проверено чтением (селекторы CSS, вызовы _activateSafeDayCycleOutline, текст документации и
теста), не браузерным прогоном: демонстрация самого белого-тайла на статической карточке
потребовала бы отдельного HA Companion WebView стенда, которого нет ни у одного участника этого
раунда; риск подтверждён структурно (тот же координатный размер, тот же непрерывный
will-change:filter), а не количественно замерен.
Находка №2 (Medium, в скоупе) — Анимированные переходы камеры (double-tap, «Вписать всё», клик по комнате, колесо/кнопки zoom) не активируют safe-режим
Файлы: src/houseplan-card.ts — _onWheel (6727), _stepZoom (6744), _resetZoom/_fitAll
(6764/6396, включая reason:'double-tap' из _doubleFit.pointerUp на строке 7044), переход по
клику на комнату (reason:'room', строка 6381) — все они вызывают _startCameraTransition(...),
которая не вызывает _activateSafeDayCycleOutline() нигде.
_activateSafeDayCycleOutline() вызывается только из трёх точек прямого pointer-move — двухпальцевый
pinch, _panLock==='pan' (drag) и multitouch-старт. Контракт п.3 ТЗ говорит «во время всей
pinch/pan-сессии», и AC1–AC4/AC9 буквально описывают только pinch — так что это не прямое
нарушение контракта (в отличие от находки №1), но double-tap-to-fit и room-fit — такие же
многокадровые анимированные изменения viewBox (CAMERA_FIT_MS), достижимые касанием на
планшете-киоске (тот же класс устройства, где воспроизведён #582), и физический механизм риска
(большой координатный will-change:filter на .hp-paperg во время многокадрового изменения
viewBox) не привязан семантически именно к жесту «два пальца» — он привязан к тому, что слой
вообще движется на экране несколько кадров подряд. Пользователь, который на планшете только
дважды тапает «показать всё» или нажимает кнопку зума и никогда не щипает/не тащит план одним
пальцем, весь сеанс сохраняет исходную #532-риск-топологию.
Не проверено запуском (нет прямого свидетеля double-tap/room-fit в новом смоке —
smoke_daycycle_layer_budget.mjs покрывает только явный touch-pinch путь); проверено чтением
всех точек входа _startCameraTransition. В скоупе задачи (тот же файл, тот же механизм) —
чинится добавлением вызова _activateSafeDayCycleOutline() в _startCameraTransition (или во все
её вызывающие точки) либо явным решением сузить контракт п.3 до буквально pinch/pan с вынесением
вопроса владельцу, почему double-tap/room-fit исключены.
Что проверено и корректно
- AC1–AC3 (layer budget, mutant AC2, presented-frame screencast) — подтверждены свежим
запуском
smoke_daycycle_layer_budget.mjs: 0 слоёв больше 4096, суммарная площадь экранного порядка, 12/12 presented-кадров без белых пикселей,.hp-paper-outline-svgvisibility:hidden→visibleровно при активацииhp-safe-daycycle-outline. - AC4 — перепроверен на новом, медианно-стабилизированном свидетеле: 1.55 при потолке 2.0
(мой собственный прогон, три чередующиеся замкнутые пары); мутант
daycycle-outline-not-promotedловит регрессию (node scripts/mutation-gate.mjs --id=...— поймано 1 из 1). - AC6 — все 172 сцены golden-матрицы, включая 4 day-cycle,
passedна каноничном Linux (мой собственный прогонnpm run golden:verify). - AC2 — новый мутант
daycycle-outline-promoted-on-inner-paperзарегистрирован на верном guard-файле и реально ловит возвратwill-change:filterна.hp-papergв safe-режиме (мой запуск, поймано 1 из 1). - Единый живой viewport (#579).
data-hp-live-viewboxстоит на.hp-paper-outline-svgтем же значением, что наplan-svg; активация safe-режима идёт напрямую черезclassList.addна_stageEl, минуя ожидание Lit re-render — сохраняет контракт п.4 (бюджетированные обновленияviewBoxбез полного re-render на каждый pointer-кадр). Проверено чтением плюсsmoke_daycycle_layer_budget(cameraActivatesSafeFallback,settledPlanLayerIsExplicitAndBounded). - Изометрия.
plan-svgстал безусловным классом ещё в r1 (принято тогда же, не новая находка);smoke_isometric_contract.mjsзелёный на этом материале. - Мутация/тест-план. Оба мутанта #582 (старый и новый) реально ловятся своими guard-смоками, не только заявлены.
- Changelog/трейлеры.
Issue: #582+User-Visible: yesнаa68e4c6b, оба changelog правлены в нём же и по-прежнему точно описывают финальное поведение основной карточки (перепроверено — ни pinch-путь, ни визуальный контур с r1 не изменились содержательно для пользователя, описание не вводит в заблуждение относительно находки №1, поскольку не упоминает static-карточку явно). Коммиты2a8b5f1d/226f3230—User-Visible: no, соответствует правилу (продуктовое поведение основной карточки не меняется относительно уже описанного, правится только устойчивость реализации/свидетеля). - Документация.
ARCHITECTURE.md/SUN.md/TOUCH-SUPPORT.md/TESTING.mdпострочно соответствуют новому коду, включая честное признание про статическую карточку (которое и стало находкой №1 — документация не скрывает пробел, она его называет). node scripts/check-docs.mjs,node scripts/process-gate.mjs --issues,npm run bundle:budget— зелёные на материале.
Унаследовано из r1
Документ: docs/reviews/CODE-REVIEW-582-r1.md (коммит 728c564e), материал r1 —
0a6592f4112fd7ceef68cbd3be7c44eeb8a7ef3e (SHA осиротел после ребейза, не резолвится в этом
чекауте — по PROCESS.md §2.10 это не находка). Принято без повторной проверки, так как дельта
r1→r2 этого не касается:
- AC7 (unit/structure, переиспользуемый рендерер бумаги).
renderPaperShapes()— тот же API, что в r1, используется в обеих поверхностях без изменений сигнатуры; существующее покрытие «пустого и составного плана» вtest/logic.test.mjs/test/wall-thickness.test.mjs(найденоgrep, не запускалось повторно отдельно отnpm test, зелёного на Validate). - Спек-ревью r1 (
SPEC-REVIEW-582-r1.md, зелёный, High:0/Medium:0) — ТЗ с тех пор не менялось (тело issue не редактировалось после спек-ревью; проверено визуально, хэш якоря спек-ревью не пересчитывался в этом раунде за неимением инструмента конвейера в среде ревью — риск низкий, раздел «Контракт поведения» текстуально совпадает с тем, что цитирует r1). - AC8 (quality gates состав) — перечень необходимых гейтов не изменился между раундами.
- AC9 (полевая приёмка) — вне этапа код-ревью по определению самого AC, не переоценивается.
- i18n/миграция/откат/риски раздела ТЗ — не затронуты дельтой, содержательно не изменились.
Чего не проверял и почему
python -m pytest tests_backend, HACS, Hassfest, geometry-parity — diff не трогаетcustom_components/**/*.py, манифесты,layout/marker.space/толщину стен.node scripts/model-invariants.mjs— diff не меняет геометрическую модель/рёбра решётки/ записи толщины, только SVG-композитинг и разметку.- Полная матрица
demo/smoke_*.mjs(250 файлов) — прогнаны 6 (два «зарегистрированная связь», два прямых поrenderPaperShapes-цепочке и два точечно выбранных из 44 «прямых» слабых совпадений). Остальные 42 «прямых» совпадения — артефакт одной длинной изменённой строки шаблона со множеством не изменившихся по смыслу свойств (_mode,_booting,_modeTransitionBusy,_decorTool,_stageElи т.п.); не прогнаны сознательно, см. таблицу выше. - Полный
performance_smoke/профили внеsmoke_daycycle_raster.mjs— AC4 уже подтверждён на целевом, более дешёвом свидетеле. npx tsc --noEmit/npm test/npm run buildотдельно отbundle:sync— приняты по зелёному Validate на этом же материале (https://github.com/Matysh/houseplan-card/actions/runs/34954286064).- Реальный HA Companion WebView на живом устройстве (AC9) — вне этапа код-ревью; это же ограничение не позволило количественно подтвердить находку №1 на статической карточке (подтверждена структурно, не количественно).
Вывод
Оба High r1 закрыты предметно: AC4 (raster ratio 1.55 против потолка 2.0, воспроизведено
самостоятельно) и AC6 (172/172 golden, включая все 4 day-cycle сцены, воспроизведено
самостоятельно) — новый двухфазный механизм (hp-safe-daycycle-outline, активируемый только
жестом) решает находки r1 без регрессии производительности контура из #532. Но при этом дельта
r1→r2 предметно убрала защиту статической карточки, которую r1 реализовывал (там она была
симметрична основной), и пункт 6 контракта ТЗ («тот же ограниченный экранными размерами механизм
применяется в houseplan-space-card») сейчас не выполнен — задокументировано самим автором как
осознанное решение, не вынесенное владельцу как продуктовая развилка. Это High, в скоупе задачи,
чинится в этой же ветке. Дополнительно найден Medium: анимированные (не pinch/pan) переходы
камеры не активируют safe-режим, что оставляет часть touch-путей (double-tap-to-fit, room-fit,
кнопки/колесо зума) в исходной риск-топологии — тоже в скоупе, тоже чинится здесь. Возврат
автору.
Материал раунда
- Ветка:
issue/582-webview-large-filter-layers, HEAD226f323073afd79a1eef9e2e5979684c9ae628f7. - Дерево материала:
dca5f4042deec2196302371f3ba4ffd6ab59c242. origin/devна момент ревью:4208ca81fe3516200a5bd80c1221597492d8fa7b(merge-base с HEAD).- Предыдущий документ:
docs/reviews/CODE-REVIEW-582-r1.md(материал0a6592f4112f, вердиктred, High 2 — SHA осиротел после ребейза, не резолвится в этом чекауте, см. §2.10).
Материал раунда
- Ветка:
issue/582-webview-large-filter-layers, коммит226f323073af— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
dca5f4042deec2196302371f3ba4ffd6ab59c242git log --all --format='%H %T' | grep dca5f4042dee - Тело issue:
4c1e1ecfe8ce205f3cfb3c44eb6028a35d0bb4358323e05ba60c6fd85b3b40ba - Вердикт конвейера:
red· High 1