| 1 | <!-- English translation of adr/0037-pfd-surface-invariants-and-roslyn-enforcement.md. Canonical Russian: ../../adr/0037-pfd-surface-invariants-and-roslyn-enforcement.md --> |
| 2 | |
| 3 | # ADR 0037: PFD — surface invariants and Roslyn enforcement (weight, input lock, channels) |
| 4 | |
| 5 | **Status:** Proposed |
| 6 | **Date:** 2026-04-12 |
| 7 | **Updated:** 2026-04-16 — [naming canon](#adr0037-naming) for strict surface: `[PfdStrict]`, `PfdStrictControl`. Details — [§ History](#adr0037-history). |
| 8 | |
| 9 | ## Related ADRs |
| 10 | |
| 11 | | ADR | Role | |
| 12 | |-----|------| |
| 13 | | [0021](0021-pfd-mfd-cockpit-attention-model.md) | PFD zone in attention model | |
| 14 | | [0036](0036-cds-channel-compositor-surface-pipeline.md) | channel → CDS → compositor → surface | |
| 15 | | [0025](0025-sdk-attention-zones-and-capabilities.md) | capabilities and zones | |
| 16 | | [0039](0039-workspace-navigation-affordances.md) | navigation affordances in attention zone | |
| 17 | |
| 18 | ## Summary |
| 19 | |
| 20 | - **PFD strict surface:** weight/input lock invariants; Roslyn analyzers. |
| 21 | - Canon **`[PfdStrict]`** / `PfdStrictControl` — attention zone geography vs strict surface ([0021](0021-pfd-mfd-cockpit-attention-model.md#adr0037-zone-vs-surface)). |
| 22 | |
| 23 | ### Outside ADR |
| 24 | |
| 25 | | Document | Role | |
| 26 | |----------|------| |
| 27 | | [`CascadeIDE.ArchitectureAnalyzers/README.md`](../CascadeIDE.ArchitectureAnalyzers/README.md) | CASCOPE*; extension — per this ADR | |
| 28 | |
| 29 | --- |
| 30 | ## Context |
| 31 | |
| 32 | In the aviation metaphor **PFD** is the primary “instrument”: dense, authoritative context picture without tab hunting ([0021](0021-pfd-mfd-cockpit-attention-model.md)). At **pure code** level Roslyn does **not** know whether a widget is “light”: weight is product policy, not an IL property. |
| 33 | |
| 34 | To **formalize** PFD discipline in code we need **explicit contracts** (attributes, base types, narrow channel subscription interfaces). Then analyzers become **invariant guards**, not heuristics on class names. |
| 35 | |
| 36 | Already fixed: layer boundaries **channel / CDS / compositor** without Avalonia in `Cockpit/Channels`, `Cds`, `Composition` ([0036](0036-cds-channel-compositor-surface-pipeline.md), CASCOPE001/002). This ADR adds a layer for components **declared as strict PFD surface** (see below: this is **not** all PFD region geography). |
| 37 | |
| 38 | <a id="adr0037-zone-vs-surface"></a> |
| 39 | |
| 40 | ## Attention zone vs strict surface (0021 vs 0037) |
| 41 | |
| 42 | **Two axes that must not be mixed:** |
| 43 | |
| 44 | | Axis | What it is | Where fixed | |
| 45 | |------|------------|-------------| |
| 46 | | **Attention zone (screen geography)** | Which **region** is the gaze anchor: forward, **PFD**, MFD, EICAS as channel, etc. What **physically** sits left/next to the editor per preset. | [0021](0021-pfd-mfd-cockpit-attention-model.md) (“Zone architecture”, anchors vs attention flow). | |
| 47 | | **Strict PFD surface (engineering contract)** | Which **code component** voluntarily takes the primary-contour “instrument” role: indicators only, allowed weight, allowed data channels. | This ADR (§1–§3), explicit markers in code. | |
| 48 | |
| 49 | **Why separate:** aviation strictness must not turn the IDE into an ascetic terminal where “you cannot even click a file in the PFD region”. Solution navigation is **“where am I”** context; banishing it to MFD only for formal “PFD = read-only” raises **tax** on gaze and context switches ([0021](0021-pfd-mfd-cockpit-attention-model.md) — switches as productivity cost). |
| 50 | |
| 51 | **Hybrid picture (“layer cake”):** in the **same** PFD attention region on screen you can have: |
| 52 | |
| 53 | - **Interactive navigation** (e.g. solution explorer, workspace anchor): user **clicks, expands nodes, picks files** — **navigation context**, not a “second forward”. These components do **not** get mandatory strict PFD surface marker **0037**; the **Roslyn analyzer for this ADR does not touch them** (no marker — no §1–§3 contract). |
| 54 | - **Strict PFD surfaces** — critical-contour indicators: agent status, EICAS/alerts, compact workspace health, etc. Mark with **`[PfdStrict]`** and/or base type **`PfdStrictControl`** ([naming canon](#adr0037-naming)). **Inside** such types the analyzer **forbids** arbitrary input, **WebView**, and other **Input Lock** and **Weight** violations (§1–§2): “slap on the wrist” for extra interactivity and heavy embeds where policy demands instruments only. |
| 55 | |
| 56 | **One line for contributors:** PFD **attention zone** may contain **interactive** navigation; components **explicitly** marked strict PFD surface (**`[PfdStrict]`** or inherit **`PfdStrictControl`**) **must** obey this ADR’s **Input Lock** and **Weight Limit** invariants. The analyzer acts **only** on the marker, not on “drawn on the left”. |
| 57 | |
| 58 | <a id="adr0037-glossary-pfd-dual"></a> |
| 59 | |
| 60 | ### Glossary (two meanings of “PFD”) |
| 61 | |
| 62 | | Term | One line | |
| 63 | |------|----------| |
| 64 | | **PFD as attention zone** | Screen region in preset ([0021](0021-pfd-mfd-cockpit-attention-model.md)): *where* the “flight context” anchor lies. Geography alone does **not** auto-apply 0037 invariants. | |
| 65 | | **Strict PFD surface** | Code component with explicit [`[PfdStrict]`](#adr0037-naming) or base `PfdStrictControl`: §1–§3 and Roslyn apply. **Not** synonymous with “everything drawn in the PFD column”. | |
| 66 | |
| 67 | **Link phrasing:** “lies in PFD zone” ≠ “under 0037 contract” until the type is marked strict. |
| 68 | |
| 69 | **“Where am I in code” navigation:** examples use **solution explorer** as a familiar anchor; not the only form. Navigation by **symbols**, **type hierarchy**, **breadcrumbs**, **file structure**, etc. can be **more effective** for some tasks — still **“where am I”** anchor per [0021](0021-pfd-mfd-cockpit-attention-model.md), not [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md) instruments until the widget is marked strict. Concrete tools and presets are **product** choice; this ADR only separates **zone geography** and **marker contract**. Product direction “multiple views and related files” — [0039](0039-workspace-navigation-affordances.md). |
| 70 | |
| 71 | ## Solution |
| 72 | |
| 73 | PFD surface invariants are set in **three** rule classes. Concrete **diagnostic IDs**, forbidden type lists, and **exceptions** come at implementation (separate commits); **meaning** of invariants is fixed here. |
| 74 | |
| 75 | <a id="adr0037-naming"></a> |
| 76 | |
| 77 | ### Naming canon for strict surface (for repo code) |
| 78 | |
| 79 | **Adopted for implementation:** |
| 80 | |
| 81 | | Element | Name | Note | |
| 82 | |---------|------|------| |
| 83 | | Attribute (use) | **`[PfdStrict]`** | Full C# type name: **`PfdStrictAttribute`**, PascalCase; **not** `[PFD_Strict]` — consistent with .NET and other repo attributes. | |
| 84 | | Base type (optional) | **`PfdStrictControl`** | Avalonia `Control` base; descendants are strict PFD surface **without** duplicating attribute on every subtype (analyzer treats **attribute on type** or **inheritance from `PfdStrictControl`** — same semantics). | |
| 85 | | Do not confuse with | **`PfdSurface`** | “surface” in ADR text is **role** in UI; no separate **`[PfdSurface]`** for “any panel in zone”; without strict marker the analyzer does not apply. | |
| 86 | |
| 87 | **In-code glossary:** XML docs on `PfdStrictAttribute` and `PfdStrictControl` should link this ADR and briefly repeat [dual PFD meaning](#adr0037-glossary-pfd-dual) (attention zone ≠ automatic contract). |
| 88 | |
| 89 | <a id="adr0037-p1"></a> |
| 90 | |
| 91 | ### 1. Weight control / dependency allowlist |
| 92 | |
| 93 | **Rule:** a view or class **explicitly marked** as **strict** PFD surface (see [above](#adr0037-zone-vs-surface); [`[PfdStrict]`](#adr0037-naming) or [`PfdStrictControl`](#adr0037-naming)) **must not** directly use known heavy containers and embeds: e.g. **virtualized grids as PFD substitute**, **WebView**, **rich ItemsControl** with arbitrary templates where policy wants primitives only. |
| 94 | |
| 95 | **Logic:** PFD in product is a **dense indicator** (text, icons, simple shapes, compact progress), not a secondary work area. The analyzer does **not** score FPS; it checks **forbidden types/namespaces** in the marked type and related AXAML (if `.axaml` / codegen analysis is wired). |
| 96 | |
| 97 | **Caveat:** generic **`Grid`** as layout is often needed; banning “all Grid” is not the goal — only a **concrete list** of heavy types and scenarios agreed with the team (allowlist of permitted controls — optional phase two). |
| 98 | |
| 99 | <a id="adr0037-p2"></a> |
| 100 | |
| 101 | ### 2. Input lock / read-only display |
| 102 | |
| 103 | **Rule:** for **strict** PFD surface (by marker) in the aviation analogy — **read display** in the typical scenario; arbitrary input and focus capture in such a component **competes** with **forward** (editor, terminal) and breaks attention model. (Navigation in PFD **without** marker — outside this rule; see [above](#adr0037-zone-vs-surface).) |
| 104 | |
| 105 | For **so marked** components the analyzer **must** flag explicit **pointer, keyboard, and focus** handlers (e.g. Avalonia input event subscriptions), except **explicitly listed** product-policy exceptions (one confirm button, safety — separate ADR/checklist). |
| 106 | |
| 107 | **Why:** keep **strict** PFD surface an **indicator**, not an interactive panel stealing input from the primary work surface. |
| 108 | |
| 109 | <a id="adr0037-p3"></a> |
| 110 | |
| 111 | ### 3. Data graph restriction (channels / streams) |
| 112 | |
| 113 | **Rule:** a **strictly marked** PFD component **may** subscribe only to **allowed** primary-contour data streams (working names: e.g. **workspace health**, **agent status**, **next step** — exact types and interfaces fixed in code and [cds-contract-v0](../design/cds-contract-v0.md) as they stabilize). |
| 114 | |
| 115 | Subscribing to **secondary** saturation streams (e.g. long **git history**, **full solution tree as sole instrument source**, **full filesystem dump** as main source for **strict** PFD surface) is **instrument role mismatch**: that content belongs to **MFD / unmarked navigation / side panels**. (Separately: **solution explorer** in PFD without 0037 marker is **not** bound to this channel list — it does not claim instrument role.) |
| 116 | |
| 117 | **Logic:** analyzer checks **types** of subscription / channel factory calls in the marked component **if** channels are **stable compile-time API** (interfaces, generic factories). String DI keys, reflection, and untyped “general bus” are **outside** this ADR — need review tests, not Roslyn-only. |
| 118 | |
| 119 | ## Consequences |
| 120 | |
| 121 | - Split **[0021](0021-pfd-mfd-cockpit-attention-model.md) (zone geography)** and **0037 (marker contract)** removes false “PFD interactive vs PFD read-only only”: interactivity is allowed in the attention region; **strictness** only on explicitly marked instruments. |
| 122 | - **Explicit** “strict PFD surface in code” discipline: without marker the analyzer is silent — **conscious** contract. |
| 123 | - Rules migrate in stages to [`CascadeIDE.ArchitectureAnalyzers`](../CascadeIDE.ArchitectureAnalyzers/README.md) (new IDs beside CASCOPE*) and **unit tests** for diagnostics like the existing test project. |
| 124 | - UX conflicts (e.g. one “OK” on PFD) resolved via **exception list** or attribute subtype, not canceling base invariant. |
| 125 | |
| 126 | ## Non-goals |
| 127 | |
| 128 | - Avionics certification or full **ARINC 661** / hardware **CDS** reproduction. |
| 129 | - Replacing **human** design review: analyzer catches **known violation classes**, not “pretty/ugly”. |
| 130 | - Automatic **`Grid`** ban without exceptions and without agreed type list. |
| 131 | |
| 132 | ## Open questions (before Accepted) |
| 133 | |
| 134 | 1. Minimal **allowlist** of channels for PFD v1 and mapping to existing `Cockpit/Channels`. |
| 135 | 2. Whether **separate** `.axaml` analysis is needed or partial class + codegen suffices. |
| 136 | |
| 137 | **Naming off the table:** canon — [`[PfdStrict]` / `PfdStrictAttribute`](#adr0037-naming) and optional [`PfdStrictControl`](#adr0037-naming); see table in that section. |
| 138 | |
| 139 | --- |
| 140 | |
| 141 | ## Change history |
| 142 | |
| 143 | <a id="adr0037-history"></a> |
| 144 | |
| 145 | | Date | Change | |
| 146 | |------|--------| |
| 147 | | — | [glossary](#adr0037-glossary-pfd-dual); navigation and [0039](0039-workspace-navigation-affordances.md). | |
| 148 | | — | “Attention zone vs strict surface” ([0021](0021-pfd-mfd-cockpit-attention-model.md)). | |
| 149 | | 2026-04-16 | [naming canon](#adr0037-naming) for strict surface: `[PfdStrict]`, `PfdStrictControl`. | |
| 150 | |