Forge
markdowndeeb25a2
1<!-- English translation of adr/0046-presentation-layout-authority-and-cockpit-invariants.md. Canonical Russian: ../../adr/0046-presentation-layout-authority-and-cockpit-invariants.md -->
2
3# ADR 0046: Cockpit CDS - policy layouts (`CockpitPresentationLayoutPolicy`) and P/F/M invariants
4
5**Status:** Accepted · Implemented
6**Date:** 2026-04-14
7
8## Related ADRs
9
10| ADR | Role |
11|-----|------|
12| [0017](0017-multi-window-workspace-and-agent-surfaces.md) | `presentation`, multi-window |
13| [0021](0021-pfd-mfd-cockpit-attention-model.md) | PFD/MFD Attention Model |
14| [0044](0044-avalonia-host-skia-agent-chat-surface.md) | UI is not the source of truth in meaning |
15| [0047](0047-cockpit-instrument-descriptor-and-slot-composition.md) | Instrument, slots |
16| [0036](0036-cds-channel-compositor-surface-pipeline.md) | CDS → composer → surface |
17
18### Implementation snapshot
19
20| Element | Meaning |
21|---------|----------|
22| — | P/F/M and coercion intent invariants in VM; `CockpitPresentationLayoutPolicy` / CASCOPE003 - see § "Solution" |
23
24---
25## Context
26
27`presentation` in `settings.toml` specifies not only the decorative geometry, but also the semantic layout of the cockpit: where the PFD, Forward and MFD are located.
28
29Problem before fixing the rules:
30
31- different inputs (menu, MCP, changing UI mode, reactive changes) changed the visibility of panels differently;
32- it was possible to get a state that contradicts the anchors of the first screen;
33- closing `MfdHostWindow` visually affected the layout as if the preset had changed.
34
35This broke the cockpit model: the user saw a “floating UI” rather than a stable P/F/M scheme.
36
37## Solution
38
39<a id="adr0046-p1"></a>
40
411. The canon of rules is placed in the CDS layer: `CockpitPresentationLayoutPolicy` (`CascadeIDE.Cockpit.Cds`).
422. The source of truth for the rules is the parsed `PresentationParseResult` (first screen). Static `PresentationLayoutAuthority` from `Services/Presentation` **cleared**; at the shell boundary, only a thin intent record remains (partial `MainWindowViewModel`, `Apply*` methods - “I want” semantics, no duplication of policy).
433. For the first screen, the following invariants apply:
44 - if there is an anchor `P`, you cannot hide the left column (`IsSolutionExplorerVisible`);
45 - if there is an anchor `M`, you cannot collapse the right column of the MFD to zero (`IsChatPanelExpanded = false`);
46 - `Forward` is interpreted as a mandatory central zone; a separate explicit toggle to disable forward is not introduced.
474. Any way to change the layout must go through the coercion policy; displaying main grid columns - through the surface composer (`MainWindowShellSurfaceCompositor`, ADR 0036 p.3):
48 - relay commands;
49 - MCP visibility commands;
50 - application of UI mode;
51 - reactive-callbacks properties to intercept direct assignments.
525. Closing/opening the second `TopLevel` (`MfdHostWindow`) only changes the surface of the M-content placement, but does not recalculate the `presentation` semantics.
53
54## Consequences
55
56- The behavior of UI, MCP and modes becomes deterministic and consistent.
57- "Impossible" states do not accumulate: policy returns them to the valid area.
58- Any new commands that affect P/F/M must be consistent with the **same** coercion policy in CDS and not bypass the composer surface.
59- On the assembly: Roslyn **CASCOPE003** (`CascadeIDE.ArchitectureAnalyzers`) - direct assignments `IsSolutionExplorerVisible` / `IsChatPanelExpanded` (and backing fields) outside the [white list of files](../../CascadeIDE.ArchitectureAnalyzers/README.md); new points - via `Apply*` / expanding the list in the analyzer.
60
61## Rejected alternatives
62
63- Allow each input (menu/MCP/mode) to have its own rules: causes logic drift.
64- Consider `presentation` only a "layout hint", and not an invariant: contradicts the cockpit model.
65- Fix only UI commands, without reactive coercion: direct assignments still bypass the rules.
View only · write via MCP/CIDE