| 1 | # ADR 0037: PFD — инварианты поверхности и проверка Roslyn (weight, input lock, каналы) |
| 2 | |
| 3 | **Статус:** Proposed |
| 4 | **Дата:** 2026-04-12 |
| 5 | **Обновлено:** 2026-04-16 — [канон имён](#adr0037-naming) для строгой поверхности: `[PfdStrict]`, `PfdStrictControl`. Подробности — [§ История](#adr0037-history). |
| 6 | ## Связанные ADR |
| 7 | |
| 8 | | ADR | Роль | |
| 9 | |-----|------| |
| 10 | | [0021](0021-pfd-mfd-cockpit-attention-model.md) | зона PFD в модели внимания | |
| 11 | | [0036](0036-cds-channel-compositor-surface-pipeline.md) | канал → CDS → композитор → поверхность | |
| 12 | | [0025](0025-sdk-attention-zones-and-capabilities.md) | capabilities и зоны | |
| 13 | | [0039](0039-workspace-navigation-affordances.md) | формы навигации в зоне внимания | |
| 14 | |
| 15 | ## Резюме |
| 16 | |
| 17 | - **PFD strict surface:** инварианты weight/input lock; Roslyn-анализаторы. |
| 18 | - Канон **`[PfdStrict]`** / `PfdStrictControl` — география зоны vs строгая поверхность ([0021](0021-pfd-mfd-cockpit-attention-model.md#adr0037-zone-vs-surface)). |
| 19 | |
| 20 | ### Вне ADR |
| 21 | |
| 22 | | Документ | Роль | |
| 23 | |----------|------| |
| 24 | | [`CascadeIDE.ArchitectureAnalyzers/README.md`](../CascadeIDE.ArchitectureAnalyzers/README.md) | CASCOPE*; расширение — по этому ADR | |
| 25 | |
| 26 | --- |
| 27 | ## Контекст |
| 28 | |
| 29 | В авиационной метафоре **PFD** — первичный «прибор»: плотная, авторитетная картина контекста без охоты по вкладкам ([0021](0021-pfd-mfd-cockpit-attention-model.md)). На уровне **чистого кода** Roslyn **не знает**, «лёгкий» ли виджет: тяжесть — это политика продукта, а не свойство IL. |
| 30 | |
| 31 | Чтобы **формализовать** дисциплину PFD в коде, нужны **явные контракты** (атрибуты, базовые типы, узкие интерфейсы подписок на каналы). Тогда анализаторы становятся **охранниками инвариантов**, а не эвристикой по имени класса. |
| 32 | |
| 33 | Уже зафиксировано: границы слоёв **канал / CDS / композитор** без Avalonia в `Cockpit/Channels`, `Cds`, `Composition` ([0036](0036-cds-channel-compositor-surface-pipeline.md), CASCOPE001/002). Этот ADR задаёт **дополнительный** слой — **именно для компонентов, декларированных как строгая PFD-поверхность** (см. раздел ниже: это **не** вся география региона PFD). |
| 34 | |
| 35 | <a id="adr0037-zone-vs-surface"></a> |
| 36 | |
| 37 | ## Зона внимания и строгая поверхность (0021 vs 0037) |
| 38 | |
| 39 | **Две оси, которые нельзя смешивать:** |
| 40 | |
| 41 | | Ось | Что это | Где зафиксировано | |
| 42 | |-----|---------|-------------------| |
| 43 | | **Зона внимания (география экрана)** | Какой **регион** окна — якорь взгляда: лобовое, **PFD**, MFD, EICAS как канал и т.д. Что **физически** стоит слева/рядом с редактором по пресету. | [0021](0021-pfd-mfd-cockpit-attention-model.md) («Архитектура зон», якоря vs attention flow). | |
| 44 | | **Строгая PFD-поверхность (инженерный контракт)** | Какой **компонент кода** добровольно берёт на себя роль «приборной части» первичного контура: только индикаторы, допустимый вес, допустимые каналы данных. | Этот ADR (§1–§3), явные маркеры в коде. | |
| 45 | |
| 46 | **Зачем разделять:** авиационная строгость не должна превращать IDE в аскетичный терминал, где «в регионе PFD нельзя даже кликнуть по файлу». Навигация по решению — **контекст «где я»**; если выселить её только в MFD ради формального «PFD = read-only», вырастет **налог** на переключение взгляда и контекста ([0021](0021-pfd-mfd-cockpit-attention-model.md) — переключения как цена продуктивности). |
| 47 | |
| 48 | **Гибридная картина («слоёный пирог»):** в **одном** регионе внимания **PFD** на экране могут сосуществовать: |
| 49 | |
| 50 | - **Интерактивная навигация** (например обозреватель решения, «якорь» workspace): пользователь **кликает, раскрывает узлы, выбирает файл** — это остаётся **навигационным контекстом**, а не «вторым лобовым». На эти компоненты **не** вешается обязательный маркер строгой PFD-поверхности **0037**; **Roslyn-анализатор по этому ADR их не трогает** (нет маркера — нет контракта §1–§3). |
| 51 | - **Строгие PFD-surfaces** — индикаторы критического контура: статус агента, EICAS/оповещения, компактное здоровье workspace и т.п. Их помечают атрибутом **`[PfdStrict]`** и/или базовым типом **`PfdStrictControl`** ([канон имени](#adr0037-naming)). **Внутри** таких типов анализатор **запрещает** произвольный ввод, **WebView** и иные нарушения **Input Lock** и **Weight** (§1–§2): «бьёт по рукам» за лишний интерактив и тяжёлые встраивания там, где политика требует только «авионики». |
| 52 | |
| 53 | **Итог одной фразой для контрибьюторов:** зона внимания **PFD** может содержать **интерактивные** элементы навигации; компоненты, **явно** помеченные как строгая PFD-поверхность (**`[PfdStrict]`** или наследование от **`PfdStrictControl`**), **обязаны** соблюдать инварианты **Input Lock** и **Weight Limit** этого ADR. Анализатор действует **только** по маркеру, а не по факту «компонент нарисован слева». |
| 54 | |
| 55 | <a id="adr0037-glossary-pfd-dual"></a> |
| 56 | |
| 57 | ### Глоссарий (два смысла «PFD») |
| 58 | |
| 59 | | Термин | Одна строка | |
| 60 | |--------|-------------| |
| 61 | | **PFD как зона внимания** | Регион экрана в пресете ([0021](0021-pfd-mfd-cockpit-attention-model.md)): *где* лежит якорь «контекст полёта». Сама география **не** включает автоматически строгие инварианты 0037. | |
| 62 | | **Строгая PFD-поверхность** | Компонент в коде с явным маркером [`[PfdStrict]`](#adr0037-naming) или базовым типом `PfdStrictControl`: на него распространяются §1–§3 и Roslyn. Это **не** синоним «всё, что нарисовано в колонке PFD». | |
| 63 | |
| 64 | **Связка для ссылок:** «лежит в зоне PFD» ≠ «подпал под контракт 0037», пока тип не помечен как строгая поверхность. |
| 65 | |
| 66 | **Навигация «где я в коде»:** в примерах выше фигурирует **обозреватель решения** как знакомый ориентир; это **не** требование единственной формы. Навигация по **символам**, **иерархии типов**, **хлебным крошкам**, **структуре файла** и т.п. может быть для части задач **эффективнее** классического дерева — и по-прежнему относится к **якорю «где я»** в смысле [0021](0021-pfd-mfd-cockpit-attention-model.md), а не к «приборам» [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md), пока на виджет не повешен маркер строгой поверхности. Конкретный набор инструментов и пресетов — **продуктовый** выбор; этот ADR только фиксирует развод **географии зоны** и **контракта по маркеру**. Продуктовое направление «несколько представлений и связанные файлы» — [0039](0039-workspace-navigation-affordances.md). |
| 67 | |
| 68 | ## Решение |
| 69 | |
| 70 | Инварианты PFD-поверхности задаём **тремя** классами правил. Конкретные **идентификаторы диагностик**, списки запрещённых типов и **исключения** вводятся при реализации (отдельные коммиты), но **смысл** инвариантов фиксируется здесь. |
| 71 | |
| 72 | <a id="adr0037-naming"></a> |
| 73 | |
| 74 | ### Канон имён для строгой поверхности (для кода в репозитории) |
| 75 | |
| 76 | **Принято для реализации:** |
| 77 | |
| 78 | | Элемент | Имя | Заметка | |
| 79 | |---------|-----|---------| |
| 80 | | Атрибут (использование) | **`[PfdStrict]`** | Полное имя типа в C#: **`PfdStrictAttribute`**, PascalCase; **не** `[PFD_Strict]` — так единообразнее с идиомой .NET и с остальными атрибутами в репо. | |
| 81 | | Базовый тип (опционально) | **`PfdStrictControl`** | Базовый класс для Avalonia-`Control`, наследники которого считаются строгой PFD-поверхностью **без** дублирования атрибута на каждом подтипе (анализатор учитывает **атрибут на типе** или **наследование от `PfdStrictControl`** — семантика одна). | |
| 82 | | Не путать с | **`PfdSurface`** | Слово «surface» в тексте ADR — про **роль** в UI; отдельного атрибута **`[PfdSurface]`** для «любой панели в зоне» не вводим: без маркера строгого контракта анализатор не применяется. | |
| 83 | |
| 84 | **Глоссарий в коде:** XML-доки на `PfdStrictAttribute` и `PfdStrictControl` должны ссылаться на этот ADR и кратко повторять [двойной смысл PFD](#adr0037-glossary-pfd-dual) (зона внимания ≠ автоматический контракт). |
| 85 | |
| 86 | <a id="adr0037-p1"></a> |
| 87 | |
| 88 | ### 1. Контроль «веса» (Weight / dependency allowlist) |
| 89 | |
| 90 | **Правило:** компонент или класс представления, **явно помеченный** как **строгая** PFD-поверхность (см. [раздел выше](#adr0037-zone-vs-surface); [`[PfdStrict]`](#adr0037-naming) или базовый тип [`PfdStrictControl`](#adr0037-naming)), **не должен** напрямую использовать заведомо тяжёлые контейнеры и встраивания: например **таблицы с виртуализацией как замена смысла PFD**, **WebView**, **богатые ItemsControl** с произвольными шаблонами там, где политика требует только примитивов. |
| 91 | |
| 92 | **Логика:** PFD в продукте — **плотный индикатор** (текст, иконки, простые формы, компактные прогресс-индикаторы), а не вторичная рабочая область. Анализатор **не оценивает FPS**; он проверяет **запрещённые типы/пространства имён** в пределах помеченного типа и связанного AXAML (если подключается анализ `.axaml` / codegen). |
| 93 | |
| 94 | **Оговорка:** универсальный **`Grid`** как layout часто необходим; запрет «всего Grid» не является целью — только **конкретный список** тяжёлых типов и сценариев, согласованный с командой (белый список разрешённых контролов — опционально во второй фазе). |
| 95 | |
| 96 | <a id="adr0037-p2"></a> |
| 97 | |
| 98 | ### 2. Запрет ввода (Input lock / read-only display) |
| 99 | |
| 100 | **Правило:** для **строгой** PFD-поверхности (по маркеру) в авиационной аналогии — **дисплей для чтения** в типичном сценарии; произвольный ввод и перехват фокуса в таком компоненте **конкурирует** с **лобовым** (редактор, терминал) и ломает модель внимания. (Навигация в регионе PFD **без** маркера — вне этого правила; см. [выше](#adr0037-zone-vs-surface).) |
| 101 | |
| 102 | Для **так помеченных** компонентов анализатор **должен** диагностировать явные обработчики **указателя, клавиатуры и фокуса** (например подписки на события ввода Avalonia), за исключением **явно перечисленных** исключений в политике продукта (одна кнопка подтверждения, safety — по отдельному ADR/чеклисту). |
| 103 | |
| 104 | **Зачем:** сохранить **строгую** PFD-поверхность **индикатором**, а не интерактивной панелью, которая отнимает ввод у первичной рабочей поверхности. |
| 105 | |
| 106 | <a id="adr0037-p3"></a> |
| 107 | |
| 108 | ### 3. Ограничение графа данных (каналы / потоки) |
| 109 | |
| 110 | **Правило:** **строго помеченный** PFD-компонент **может** подписываться только на **разрешённые** для первичного контура потоки данных (рабочие имена: например каналы **состояния здоровья workspace**, **статуса агента**, **следующего шага** — точный перечень типов и интерфейсов фиксируется в коде и в [cds-contract-v0](../design/cds-contract-v0.md) по мере стабилизации). |
| 111 | |
| 112 | Подписка на потоки **вторичной** насыщенности (условно: длинная **история git**, **полное дерево решения как единственный источник данных прибора**, **полный дамп файловой системы** как основной источник **именно строгой** PFD-поверхности) считается **несоответствием роли прибора**: это контент **MFD / навигации без маркера** / боковых панелей. (Отдельно: **обозреватель решения** в регионе PFD без маркера 0037 **не** обязан ограничиваться этим списком каналов — он не претендует на роль «прибора».) |
| 113 | |
| 114 | **Логика:** анализатор проверяет **типы** вызовов подписки / конструкторов каналов в пределах помеченного компонента, **если** каналы выражены как **стабильные compile-time API** (интерфейсы, обобщённые фабрики). Строковые ключи DI, рефлексия и «общий» bus без типизации **вне зоны** этого ADR — там нужны обзорные тесты и ревью, а не Roslyn-only. |
| 115 | |
| 116 | ## Последствия |
| 117 | |
| 118 | - Разделение **[0021](0021-pfd-mfd-cockpit-attention-model.md) (география зоны)** и **0037 (строгий контракт по маркеру)** снимает ложное противоречие «PFD интерактивен vs PFD только read-only»: интерактив допустим в регионе внимания, **строгость** — только у явно помеченных «приборов». |
| 119 | - Появляется **явная** дисциплина «строгая PFD-поверхность в коде»: без маркера анализатор молчит — это **осознанный** контракт. |
| 120 | - Правила **порционно** переносятся в [`CascadeIDE.ArchitectureAnalyzers`](../CascadeIDE.ArchitectureAnalyzers/README.md) (новые ID рядом с CASCOPE*) и **юнит-тесты** на диагностики по образцу существующего проекта тестов. |
| 121 | - Конфликты с UX (например одна кнопка «OK» на PFD) разрешаются **списком исключений** или отдельным подтипом атрибута, не отменяя базовый инвариант. |
| 122 | |
| 123 | ## Не цели |
| 124 | |
| 125 | - Сертификация авионики или полное воспроизведение **ARINC 661** / **CDS** железа. |
| 126 | - Замена **человеческого** ревью дизайна: анализатор ловит **известные классы нарушений**, не «красиво/некрасиво». |
| 127 | - Автоматический запрет **`Grid`** без исключений и без согласованного списка типов. |
| 128 | |
| 129 | ## Открытые вопросы (до статуса Accepted) |
| 130 | |
| 131 | 1. Минимальный **allowlist** разрешённых каналов для PFD v1 и маппинг на существующие `Cockpit/Channels`. |
| 132 | 2. Нужен ли **отдельный** анализ `.axaml` или достаточно partial-класса и codegen. |
| 133 | |
| 134 | **Снято с повестки именования:** канон — [`[PfdStrict]` / `PfdStrictAttribute`](#adr0037-naming) и опционально [`PfdStrictControl`](#adr0037-naming); см. таблицу в том же разделе. |
| 135 | |
| 136 | --- |
| 137 | |
| 138 | ## История изменений |
| 139 | |
| 140 | <a id="adr0037-history"></a> |
| 141 | |
| 142 | | Дата | Изменение | |
| 143 | |------|-----------| |
| 144 | | — | [глоссарий](#adr0037-glossary-pfd-dual); навигация и [0039](0039-workspace-navigation-affordances.md). | |
| 145 | | — | раздел «Зона внимания vs строгая поверхность» ([0021](0021-pfd-mfd-cockpit-attention-model.md)). | |
| 146 | | 2026-04-16 | [канон имён](#adr0037-naming) для строгой поверхности: `[PfdStrict]`, `PfdStrictControl`. | |
| 147 | |