Protocol verification matrix — advertised vs implemented vs tested#
Last refreshed: 2026-07-13 (main)
Companion doc:protocol-comparison.md answers
"what does each compositor advertise?" (cross-project surface count).
This doc answers a different, internal question: "for every protocol
margo advertises, how do we know it actually works?" — the gap between
"the global is in the registry" and "a client round-trips against it in CI."
This is a maturity ledger, not a marketing sheet. A protocol can be
advertised ✅ and implemented ✅ yet have no automated coverage — that
is exactly the row this table exists to surface, so the snapshot/integration
backlog (road_map.md §15.2, test coverage) is driven by evidence, not vibes.
How the columns are derived (so they stay honest)#
Column
✅ means
Source of truth
Advertised
global bound into the registry at startup
State::new in margo/src/state.rs (the *State::new::<Self>(&dh) calls) + the delegate_*! macro set
Implemented
real handler logic, not an advertise-only stub
the *Handler impls / margo/src/protocols/*.rs for hand-rolled ones; ⚠️ = partial (read-only, or a documented caveat)
Runtime-tested
a live Wayland client exercises it
🟢 headless integration fixture in margo/src/tests/ (real MargoState + nested client over a UnixStream, run in CI); 🟡 manual on-hardware only (manual-checklist.md); ❌ neither
Unit / snapshot-tested
pure-unit or insta snapshot coverage
#[test] in the owning module / insta::assert_snapshot!; ❌ = none
Known gaps
the caveat a maintainer must know
—
Why two test columns. They catch different bug classes, and margo runs
both halves (see margo/src/tests/fixture.rs for the rationale, paraphrasing
niri's "~5,280 snapshots and a fixture harness"):
Unit / snapshot locks pure logic — layout arithmetic, LUT generation,
schedule math. The layout engine alone has 38 insta text snapshots
(margo/src/layout/snapshots/) plus the colour/twilight unit suites
(render/icc_lut, render/hdr_metadata, twilight/*). These don't touch
the wire.
Runtime (integration fixture) locks protocol behaviour over a real
connection — wl_registry.bind, wl_display.sync, server dispatch order,
configure-event sequencing. This is the only column that proves the
advertised global is wired to a working handler.
🟢 (CI integration) is strictly stronger evidence than 🟡 (manual): 🟢 runs on
every push and can't silently rot; 🟡 depends on a human walking the checklist
on real DRM hardware that GitHub runners can't provide.
The registry globals every Wayland client expects. globals.rs (3 tests)
asserts the advertised set, so the advertisement of this whole block is
runtime-checked even where the per-protocol behaviour isn't.
Protocol
Advertised
Implemented
Runtime-tested
Unit/snapshot
Known gaps
wl_compositor / wl_subcompositor
✅
✅
🟢 globals.rs
❌
—
wl_shm
✅
✅
🟢 globals.rs
❌
—
wl_seat
✅
✅
🟢 globals.rs
❌
seat capability/focus paths exercised indirectly via xdg_shell.rs
Advertised & implemented: every protocol in this matrix (no advertise-only
stubs — ⚠️ rows are partial behaviour, not missing handlers).
Runtime-tested in CI (🟢): the protocol areas above via margo/src/tests/
(globals, xdg_shell, xdg_decoration, xdg_activation, layer_shell,
session_lock, xwayland_shell, pointer_constraints, output_management,
color_management, gamma_control, screencopy, dmabuf, idle,
selection) plus the multi-monitor internal paths (output_assignment,
focus_mon, tag_mon) — the latter drive a real focused toplevel through the
fixture (see add_keyboard).
Unit / snapshot: concentrated in the render/colour/twilight stack and
the layout engine (38 layout snapshots) — pure-logic surfaces. The wire
protocols mostly rely on the integration fixture instead, by design.
🟡 manual-only / ❌ untested: the long tail (input extensions, capture
loop, sync/timing, security-context, foreign-toplevel/workspace). These are
the snapshot/integration backlog.
Next integration-fixture targets (the riskiest untested paths)#
Ordered by blast radius, matching the agreed first wave. The first two
landed (2026-06-01); the remainder are the live backlog:
~~tag move across outputs (tagmon) — highest-risk because it mutates
per-output state in one step.~~ ✅ tag_mon.rs (4 tests).
layer-shell popup / menu grab + dismiss — fixture has the surface, not the
popup-grab path.
focus restore after unmap / lock — session_lock.rs covers lock, not the
post-unlock focus stack. (add_keyboard fixture helper, added for tag_mon,
now unblocks this.)
floating-over-tiled stacking + focus.
scroller offscreen focus (focus a window scrolled out of view) — the
overview has 6 tests; the offscreen-focus scroll-into-view path is separate.
See road_map.md §15.2 (test coverage) for the running backlog; this matrix is the
evidence table that feeds it.