ADR 0055: Skia instrument composition pipeline (Intent → Declutter → Layout → Render)¶
Status: Accepted
Date: 2026-04-17
Related ADRs¶
| ADR | Role |
|---|---|
| 0036 | Channel → CDS → surface |
| 0047 | Instrument / slot |
| 0049 | Phased Skia rollout |
| 0053 | Semantic map, control flow |
| 0056 | First consumer — intent map |
| 0064 | Render stage, deck primitives |
Summary¶
- Shared Skia instrument pipeline: Intent → Declutter → Layout → Render.
- Common invariants for semantic map, deck, and future instruments.
- Consumers — 0056, 0057.
Context¶
Skia instruments in CIDE (workspace_navigation_map, future instruments and overlays) are placed via the host surface compositor, but each instrument’s internal composition (data, declutter, geometry, render rules) was historically mixed into a single feature.
That produced two systemic effects:
- Blurred responsibility boundary: “surface” and “instrument internals” get conflated in discussion and code.
- Every new Skia feature risks reinventing its own mini-pipeline without shared invariants.
- With
controlFlowper 0053, readability on a small viewport degrades without a separate declutter layer.
Decision¶
1) Introduce one pipeline for all Skia instruments¶
Each Skia instrument fixes an internal sequence:
- Intent: build the instrument’s domain state model (source of truth for content).
- Declutter: policy filtering/aggregation and prioritization before geometry.
- Layout: geometric layout.
- Render: draw the finished scene without business logic.
The surface compositor stays an outer layer and answers only “where the instrument is mounted” (slot/surface), not internal geometry/declutter.
2) Internal instrument compositor is mandatory¶
Each instrument introduces a dedicated compositor (e.g. *Compositor) that:
- selects a layout engine by domain detail level;
- applies viewport height/density policy;
- returns a finished scene and display parameters.
This is the canonical path for new Skia features so logic is not duplicated across VMs and controls.
3) Declutter — shared policy layer, not layout ifs¶
Noise-reduction rules (density, repeat aggregation, main scenario over secondary detail — for graphs: main flow vs secondary edges) live in a separate policy layer with one interface across instruments.
Layout must not decide “what to hide”, only “how to lay out what was already selected”.
4) Layout / Render: readability invariants (HF / cockpit guidance)¶
Below are instrument-agnostic rules for any Skia instrument with the §1 pipeline (chat timeline, scale, control-flow graph, schematic, etc.). Parenthetical examples illustrate a case; canonical control-flow detail — 0053. Cockpit attention model: 0021, 0046.
-
Attention hierarchy and draw order (Render). The instrument defines a fixed layer order for the base scene: background/grid and “structural” primitives first, then main content, then labels and auxiliary markup within their zones. Attention signals from CDS/channels (0036) — highlights, traces, health — draw on top of that base without reshaping domain geometry for an “alert”. Interactive focus (cursor, selection) is the top readable layer over static content. (Example: graph — edges → nodes → labels in the band; chat — branch background → bubbles → timestamps.)
-
Noise budget: Declutter chooses what, Layout limits how much fits. Declutter sets visible entities and aggregation; Layout applies density and geometric ceilings for the viewport (minimum gaps between blocks, row/column limits, reserves for auxiliary columns). Do not compensate overload with scale alone when policy already signals “too much” — reduce volume or detail level first. (Example: graph — vertical step and band width; chat — visible message count and preview height.)
-
Detail levels (Glance / Normal / Inspect) — primarily Declutter, not geometry alone. Domain detail level (including shared
glance/normal/inspectfrom the adoption plan) sets selection and aggregation before Layout. Layout changes layout metrics but does not replace the decision “how many entities between Glance and Inspect”. (Example:CodeNavigationMapDetailLevelfor the map; another instrument — its own enum/scale, same role split.) -
Readability floors in Glance (Render / theme). When compressing the viewport, keep lower bounds on readability: glyph size, line weight, outline contrast — so “glance from the side” does not become indistinguishable noise. Numbers come from theme/instrument constants; invariant — do not sacrifice distinguishability to fit the rect. (Example: minimum link thickness and node outlines; minimum list row height.)
-
Stable spatial roles (Layout). Labels, legends, scales, and other auxiliary graphics anchor to the main content zone (adjacent area, fixed offset from the “data band”), not arbitrary viewport corners — lower cost to match label to object. (Example: legend beside the graph band 0053; axis label at the edge of the same region as the data.)
-
Alerting on top of the base. Channel compositors (TraceFlow, health, etc.) add an overlay to the instrument’s assembled base scene: they do not duplicate Intent/Layout of the domain model, only show attention/trace/status state on top — for any base instrument type.
Contract checks (pipeline tests): on a small viewport — no instrument-forbidden layout-primitive overlaps (collisions, unreadable stacking) at the declared detail level; when moving to a deeper level (e.g. Glance → Inspect) useful information must not shrink without explicit policy degradation (empty state, data error). What counts as a collision — in each instrument’s contract.
Consequences¶
Pros¶
- Clear architectural boundary: host surface vs instrument internals.
- One contract for all future Skia features and instruments.
- Declutter/layout/render can evolve independently without regressions in slot composition.
Cons¶
- More entities and contracts in the Skia instrument layer.
- Separate pipeline-level tests needed (not only layout unit tests).
Non-goals¶
- Do not change ADR 0036 and 0047: external surface composition stays as is.
- Do not fix here the final UI style of all edge cases and all instruments (that evolves within pipeline and policy).
Adoption plan (minimum)¶
- Fix the base contract for the shared Skia instrument pipeline.
- Canonize
CodeNavigationMapCompositoras the first adapter of this contract. - Extract a shared
DeclutterPolicyinterface (minimum:glance,normal,inspect) and instrument-specific implementations. - Fix pipeline contract tests:
- stable scene composition for the same input;
- readability on a small viewport (no critical collisions/stacking per instrument contract);
- correct degradation on incomplete input (no false content).