Перейти к содержанию

ADR 0046: Cockpit CDS — policy раскладки (CockpitPresentationLayoutPolicy) и инварианты P/F/M

Статус: Accepted · Implemented
Дата: 2026-04-14

Связанные ADR

ADR Роль
0017 presentation, мультиоконность
0021 Модель внимания PFD/MFD
0044 UI не источник истины по смыслу
0047 Instrument, слоты
0036 CDS → композитор → поверхность

Снимок реализации

Элемент Значение
инварианты P/F/M и coercion intent в VM; CockpitPresentationLayoutPolicy / CASCOPE003 — см. § «Решение»

Контекст

presentation в settings.toml задает не только декоративную геометрию, а семантическую раскладку кабины: где находятся PFD, Forward и MFD.

Проблема до фиксации правил:

  • разные входы (меню, MCP, смена UI-режима, реактивные изменения) по-разному меняли видимость панелей;
  • можно было получить состояние, которое противоречит якорям первого экрана;
  • закрытие MfdHostWindow визуально влияло на раскладку так, будто пресет изменился.

Это ломало модель "кабины": пользователь видел "плавающий UI", а не устойчивую схему P/F/M.

Решение

  1. Канон правил размещается в слое CDS: CockpitPresentationLayoutPolicy (CascadeIDE.Cockpit.Cds).
  2. Источник правды для правил — разобранный PresentationParseResult (первый экран). Статический PresentationLayoutAuthority из Services/Presentation снят; на границе shell остаётся только тонкая запись intent (частичный MainWindowViewModel, методы Apply* — семантика «хочу», не дублирование policy).
  3. Для первого экрана действуют инварианты:
  4. если есть якорь P, нельзя скрыть левую колонку (IsSolutionExplorerVisible);
  5. если есть якорь M, нельзя свернуть правую колонку MFD в ноль (IsChatPanelExpanded = false);
  6. Forward трактуется как обязательная центральная зона; отдельный explicit toggle для отключения forward не вводится.
  7. Любой путь изменения раскладки должен проходить через coercion policy; отображение колонок main grid — через композитор поверхности (MainWindowShellSurfaceCompositor, ADR 0036 п.3):
  8. relay-команды;
  9. MCP-команды видимости;
  10. применение UI-режима;
  11. reactive-callbacks свойств, чтобы перехватывать прямые присваивания.
  12. Закрытие/открытие второго TopLevel (MfdHostWindow) меняет только surface размещения M-контента, но не пересчитывает семантику presentation.

Последствия

  • Поведение UI, MCP и режимов становится детерминированным и согласованным.
  • "Невозможные" состояния не накапливаются: policy возвращает их в валидную область.
  • Любые новые команды, влияющие на P/F/M, обязаны согласовываться с той же coercion policy в CDS и не обходить композитор поверхности.
  • На сборке: Roslyn CASCOPE003 (CascadeIDE.ArchitectureAnalyzers) — прямые присваивания IsSolutionExplorerVisible / IsChatPanelExpanded (и бэкинг-полей) вне белого списка файлов; новые точки — через Apply* / расширение списка в анализаторе.

Отклоненные альтернативы

  • Разрешить каждому входу (меню/MCP/режим) иметь собственные правила: приводит к дрейфу логики.
  • Считать presentation только "подсказкой layout", а не инвариантом: противоречит модели кабины.
  • Чинить только UI-команды, без реактивного coercion: прямые присваивания все равно обходят правила.