Desktop-environment parity model
desktop-environment specs/desktop-environment/parity-model.kmd
Modelo normativo de paridade de desktop-environment (Kolide ↔ referência, p.ex. GNOME Shell): enumera a superfície canônica de um DE como `parity_role`s auditáveis, cada um com referência externa + fonte-de-verdade (gschema / estrutural-AT-SPI / visual-motion) + veredito (gap-a-fechar vs divergência intencional). É o "manifesto do shell" que o /k-mirror consome para fazer paridade shell-vs-shell, generalizando os parity_roles dispersos (overview.meta.json) e a gap-analysis em prosa (kolide#042) numa matriz única.
When this spec applies
All triggers
- Implementar/auditar uma feature de desktop-environment no Kolide (launcher, overview, hotcorner, workspaces, painel, context-menu, quick-settings, notificações, lock)
- Avaliar paridade do Kolide contra o GNOME (ou outro DE de referência)
- Decidir se uma diferença Kolide↔GNOME é gap-a-fechar ou divergência intencional de design
Specification body
Desktop-environment parity model
1. Escopo e motivo
Um desktop environment (DE) não é um app: não tem uma única tela nem um
Navigator — é um conjunto de superfícies persistentes e modais (painel,
launcher, overview, hotcorner, workspaces, menus de contexto, quick-settings,
notificações, lock). As specs de UI existentes (app-layout/*, navigation/*,
koder-app/behaviors.kmd) governam apps, não o shell. O ui-quality-score
pontua uma surface, não um DE inteiro. Logo não havia, até aqui, uma rubrica
normativa de o que torna um DE completo nem de como medir paridade contra
uma referência (GNOME).
Esta spec preenche essa lacuna. Ela define o modelo de DE-paridade: a
superfície canônica de um DE enumerada como parity_roles, cada um amarrado a
(a) o comportamento de referência externo, (b) a fonte-de-verdade que o
governa, e (c) o veredito de paridade. O alvo concreto é o Kolide (o DE
da Koder Stack) contra o GNOME Shell como referência — mas o modelo é
neutro de referência.
Vocabulário (stack-RFC-021): kompose = o Mutter (compositor) · Kolide = o GNOME Shell (o DE) · Kroma = o GTK (toolkit) · Kodix = o OS/ISO.
2. O parity_role (unidade do modelo)
Cada superfície/affordance do DE é um parity_role com id dotted
<surface>.<affordance> (ex.: overview.search, panel.clock,
contextmenu.desktop). Schema:
parity_role:
id: "<surface>.<affordance>" # estável; chave do modelo
surface: panel|launcher|overview|hotcorner|workspaces|dock|
quicksettings|notifications|contextmenu|lock|settings|window
gnome_ref: "<comportamento de referência em 1 linha>"
governs: [config|structural|visual|motion|keybinding] # dimensões aplicáveis
source_of_truth: # COMO a referência é extraída (§4)
- gschema: ["org.gnome.shell", "org.gnome.mutter", ...] # config/keybindings
- structural: "at-spi" # árvore de elementos (shell vivo)
- visual: "reference/<impl>/<surface>.png" # screenshot/motion auditado
kolide:
status: done|partial|absent
impl: "<arquivo:linha ou - >"
ticket: "<kolide#NNN ou - >"
parity_kind: gap-to-close | intentional-divergence
divergence_rationale: "<obrigatório se intentional-divergence; senão omitir>"
A instância viva (status real por role) vive no manifesto machine-readable
do componente: infra/linux/kolide/parity/de-parity.json — consumido pelo
/k-mirror e gerado/atualizado a cada fatia de paridade. Esta spec define o
schema e a taxonomia; o .json carrega o estado.
3. Taxonomia canônica da superfície de um DE
O conjunto mínimo de parity_roles que um DE moderno deve cobrir (o
checklist de "DE completo"). Ausência de um role = status: absent explícito,
nunca omissão silenciosa.
| Surface | parity_roles | gschema de referência |
|---|---|---|
| panel | panel.clock · panel.status · panel.activities | org.gnome.desktop.interface |
| launcher | launcher.appgrid · launcher.search (type-to-search) · launcher.search_providers (provedores plugáveis: calc/arquivos/settings/caracteres) | org.gnome.desktop.search-providers + /usr/share/gnome-shell/search-providers/*.ini |
| overview | overview.activities · overview.search · overview.dash · overview.workspace_strip · overview.window_thumbnails | org.gnome.shell · org.gnome.mutter |
| hotcorner | hotcorner.topleft (+ ripple/motion) | org.gnome.desktop.interface enable-hot-corners |
| workspaces | workspaces.switch · workspaces.dynamic | org.gnome.mutter dynamic-workspaces · org.gnome.desktop.wm.keybindings |
| dock | dock.dash | (design Koder — divergência possível) |
| quicksettings | quicksettings.toggles (wifi/bt/volume/brightness/power) | org.gnome.settings-daemon.plugins.* |
| notifications | notifications.center · notifications.osd · notifications.dnd | org.gnome.desktop.notifications |
| contextmenu | contextmenu.desktop · contextmenu.window · contextmenu.dock | (construído em código — só estrutural/AT-SPI) |
| lock | lock.screen | org.gnome.desktop.screensaver |
| settings | settings.control_center · keybindings.editor | (agregado GNOME Settings ∪ Tweaks) |
| window | window.management (focus/move/resize/tile) | org.gnome.desktop.wm.keybindings · org.gnome.mutter |
Roles novos podem ser adicionados; remover um role da taxonomia exige justificativa (é afirmar que deixou de ser parte de "DE completo").
4. As três fontes-de-verdade (e a que dimensão cada uma serve)
A "verdade GNOME" não deve ser transcrita à mão de screenshots (era o defeito
— kolide#042/#044). Extrai-se por três coletores, cada um cobrindo dimensões
distintas do /k-mirror:
- gschema (config + keybindings) —
koder-app-inventory gtk-gschemasobreorg.gnome.shell/mutter/desktop.wm.keybindings/desktop.interface+ enumeração de/usr/share/gnome-shell/search-providers/*.ini. Determinístico, seguro (sem processo vivo), cobre D2 (config) e a superfície de keybindings. É a maior fatia de "paridade de comportamento" e é o coletor preferido. - estrutural (AT-SPI) — dump da árvore de a11y do shell rodando (o
gnome-shell é provider AT-SPI), em sessão GNOME descartável off-laptop
(s.khost1), nunca no laptop — o walk AT-SPI é frágil. Cobre D1 (estrutura)
e D3 (menus/popups construídos em código — ex.: os
contextmenu.*, que o gschema não vê). É a peça que ooverview.meta.jsonjá pediu por nome ("validar ESTRUTURALMENTE via shell layout introspection"). - visual/motion — screenshot/curva de referência auditada
(
tests/regression/motion/__reference__/<impl>/). Cobre D4 (visual) e a velocidade/curva (D5). Continua sendo capturada (a referência visual é legítima), mas deixa de ser a única fonte — vira a camada de pixel/tempo sobre as duas estruturais acima.
O lado Koder é introspectado simetricamente: AT-SPI sobre o kolide-shell
(GTK4, também provider AT-SPI) hoje; quando o Kolide migrar para Kroma
(endgame stack-RFC-021), o Kroma introspection-export (kroma#088,
KROMA_INTROSPECT=) substitui o AT-SPI no lado Koder — runtime-accurate, sem
fragilidade.
5. Veredito de paridade (política — selective parity)
Paridade de DE é seletiva, não total (decisão owner 2026-06-07, kolide#042):
o Kolide é GNOME-inspired, não clone (ex.: dock flutuante persistente à la
macOS/elementary vs overview-centric do GNOME 48). Por isso cada role tem
parity_kind:
- gap-to-close — divergência indesejada; vira ticket no backlog do Kolide até
status: done. - intentional-divergence — diferença de design deliberada; exige
divergence_rationalee não é tratada como gap. Marcar uma divergência como intencional é owner-decision (§Regra 15 do /k-go) — não auto-decidir.
O /k-mirror no nível de shell reporta as diferenças por role; a
classificação gap-vs-divergência é revisada com o owner, não inferida pelo
diff.
6. Integração com o /k-mirror (shell-level)
O /k-mirror hoje é app-vs-app (ex.: kterm vs tilix). Esta spec o estende ao
nível de DE: /k-mirror kolide --reference gnome-shell consome o
de-parity.json como a lista de roles, roda os três coletores (§4) por role,
produz a parity-matrix.json por parity_role × {D1..D5}, e o osinject do
Kover (XTEST/wlr-virtual-pointer) dirige cada ação para medir D5
(velocidade). A diferença para o k-mirror app-level é só o substrato de
enumeração (este modelo) e o alvo (o shell inteiro, não uma janela de app).
7. Endgame (stack-RFC-021)
A paridade-GNOME é o caminho interino. O endgame é o Kolide renderizado sobre Kroma, quando o lado Koder passa a ser auto-introspectável (Kroma export) e a referência GNOME deixa de ser necessária como muleta de implementação. Até lá, este modelo é a fonte de verdade da paridade; depois dele, a taxonomia de roles (§3) permanece válida como checklist de completude do DE, só trocando o coletor do lado Koder (AT-SPI → Kroma export).
8. Pendência de integração na Stack
Esta spec deve ser registrada no registries/spec-triggers.kmd (SSOT dos
gatilhos ação→spec) para que "implementar/auditar feature de DE no Kolide" a
dispare automaticamente — ver §triggers do frontmatter. Wiring = próximo passo
(rodar koder-spec-audit triggers --write).