12. Cockpit je šestá projekce registru, ne aplikace nad ním
Kontext
Program už odpovídá na jednu otázku pěti způsoby. Schopnost se deklaruje jednou pomocí capability! a nástroje a zdroje MCP, trasy HTTP, dokument OpenAPI, shell Swagger UI a příkazová řádka se z té deklarace odvozují (ADR 2, ADR 4). Každý z nich je určen strojům. Kdo chce vědět, co tento repozitář drží, co kde platí, co se z čeho odvozuje a co selhává, má dokument JSON, formulář Swaggeru a terminál.
Zjevný způsob, jak tu mezeru zavřít, je špatný. Dashboard, který načte /api/v1/capabilities a rozloží ho v klientské aplikaci, je druhý model stejných faktů: potřebuje vlastní seznam stránek, vlastní představu o tom, co je schopnost, vlastní kontroly zdraví, vlastní odvození grafu a vlastní odpověď, když se oba rozejdou. Každé z toho je kopie a první pravidlo tohoto repozitáře o rozhraních (project.interfaces-are-projections) existuje proto, že kopie se rozejdou při první úpravě, která na některou zapomene.
Druhé pokušení je jemnější: vrstva pohledu, která sahá přímo do indexu, protože je ve stejném procesu. To je rychlejší napsat a je to čtvrtý způsob čtení repozitáře, mimo executor, mimo cache, mimo čítače a mimo validaci, kterou procházejí všichni ostatní volající.
Dvě věci také skutečně chyběly, ne jen nebyly vykresleny. Grafy repozitáře — závislosti pravidel, rozhodnutí a to, co uvádějí v platnost, tvar vrstvy — odvozovaly shellové skripty pro web a pro spustitelný soubor nic, takže proces, který drží index, nemohl odpovědět na „co na tom závisí“. A „je to zdravé“ zodpovídaly tři samostatné příkazy (capabilities validate, bench coverage --check, generate --check) a žádná jediná čitelná věc.
Rozhodnutí
-
Cockpit je projekce.
apps/majordomus-cli/src/cockpit/vykresluje HTML renderované serverem pod/cockpita každá stránka je sestavena z toho, co schopnost odpověděla přesContext::execute— tentýž hovor dělají MCP i trasy HTTP. Stránka se počítá v týchž počítadlech výkonu, odpovídá ze stejné cache a váže ji stejná validace jako každého jiného volajícího. Žádná stránka nečte index přímo. -
Jeho routes jsou infrastruktura, ne schopnosti.
/cockpitse řadí k/,/openapi.json,/docsa/mcpvINFRASTRUCTURE_ROUTES. Nedeklaruje žádné schéma, žádnou operaci ani vlastní jméno a dokument OpenAPI jej uvádí jako to, čím je. -
Nic v něm není seznam. Katalogy navigace jsou moduly registru, druhy indexu a odvození grafů. Průzkumník schopností je filtrovaný registr. Formulář runneru se generuje ze vstupního schématu a odesílá na vlastní trasu schopnosti — žádný endpoint runneru ani druhá validace neexistuje. Příklady na stránce schopnosti jsou její vlastní
BenchmarkCases. Paleta příkazů čte/api/v1/capabilities,/api/v1/graphsa/api/v1/objectsa vlastní stránky Cockpitu z navigace, kterou server už vykreslil. -
Grafy jsou kanonické v Rustu.
graph::Graphje model uzlů a hran s deklarovanými slovníky uzlů a hran, odvozený z registru a indexu a ničeho jiného:registry,layer,rules,adrs,use-cases.graph.listagraph.getjsou běžné schopnosti, takže grafy dosáhnou na MCP, HTTP, OpenAPI a Cockpit naráz. Knihovna pro kreslení je konzument na hranici; převod do tvaru Cytoscape se děje v jedné funkci v jednom souboru JavaScriptu a budoucí projekce do Mermaid nebo Graphviz žádnou sémantiku nestaví znovu. -
Zdraví deleguje a nikdy nerozhoduje.
health.reporthlásí jednu kontrolu na dimenzi, každou s enginem, který o ní rozhodl, a příkazem, který ji reprodukuje: vlastní diagnostika indexu, builder registru, deklarovaný rozsah, git, pokrytí projekce měření výkonu a commitnutý manifest registru porovnaný s tím, co tento proces sestavil. Nepočítá žádný vlastní verdikt a nevykresluje projekce znovu při každém volání — to by znovu sestavovalo kanonický stav na každý požadavek, což tento spustitelný soubor nedělá;generate --checkzůstává úplnou odpovědí a kontrola ji jmenuje. -
Vrstva prohlížeče je vylepšení v přísném smyslu. Každá stránka je úplné HTML: stránky grafů nesou každý uzel a každou hranu jako tabulky, stránka topologie říká, kde jsou tytéž fakty jako text. Alpine přidává paletu a téma; Cytoscape, Three.js a p5 přidávají vždy jeden pohled a načítá je jen stránka, která ho používá. Pokud se nenačte žádná z nich, navigace, inspekce, zdraví i vyvolání stále fungují.
-
Politika je přísná a build je čistý vůči CSP. Každá stránka nese
default-src 'none'; script-src 'self' 'sha256-…'— žádnéunsafe-inline, žádnéunsafe-eval— proto se vendoruje CSP build Alpine a proto každý atributx-v Rustu pojmenovává vlastnost nebo metodu, ne výraz. Značkování je strom hodnot s escapováním ze samé konstrukce, ne šablonový řetězec. -
Assety žijí v
share/cockpit/, jako všechno ostatní, co tento nástroj čte za běhu. Stylesheet se kompiluje z jednoho zdrojového souboru pomocíscripts/cockpit-assetsa commituje se;alpine.csp.min.jsse commituje, protože na něm interakce závisí a je malý; Cytoscape, Three.js a p5 se při buildu vendorují a necommitují, protože jsou to dva megabajty, které nic na kritické cestě nepotřebuje. Node je závislost buildu a nikdy runtime. -
Stránky jsou cíle měření výkonu. Úvodní stránka, tabulka schopností a stránka grafu jsou deklarovány vedle
GET /aGET /docsjako systémové cíle, takže je pokrytí měření výkonu počítá a základní linie na nich může regredovat.
Zamítnuté alternativy
- Klientská aplikace (React, Vue, Svelte). Držela by doménový stav backendu na druhém místě, potřebovala by vlastní toolchain buildu za běhu a první vykreslení by záviselo na JavaScriptu. Nic, co Cockpit dělá, nepotřebuje virtuální DOM.
- Samostatný serverový proces pro UI. Druhý proces by potřeboval vlastní kopii indexu nebo HTTP skok pro každý fakt a lease sdíleného serveru už dává jeden server na repozitář.
- Šablonovací engine (Askama, MiniJinja). Nová závislost kvůli interpolaci řetězců, a interpolace je přesně místo, kde se zapomíná na escapování. Builder elementů má sto řádků, escapuje z principu a jeho testy jsou bezpečnostní testy.
- Stahování knihoven dodavatelů z CDN. Shell Swagger UI to už dělá a je to jediná část projekce HTTP, která není dostupná offline. Opakovat to pro Cockpit by učinilo z lokálního vývojářského nástroje závislost na síti.
- Znovuvykreslení každé commitnuté projekce uvnitř
health.report. Odpovídá přesně na „co je zastaralé“ a při každém požadavku přestavuje kanonický stav, což pravidlo horké cesty zakazuje a testovací sada to zachytí. - Volat
majordomus doctorpro stránku zdraví. Doctor shellového nástroje rozhoduje, zda je Majordomus zapojen do tohoto repozitáře; enginy v Rustu rozhodují, zda je to, co tento proces obsluhuje, v pořádku. Jsou to různé předměty a server v Rustu shell nevolá.
Důsledky
Přidání schopnosti přidá stránku Cockpitu, položku navigace, položku vyhledávání, položku palety, generovaný formulář, operaci OpenAPI a cíl měření výkonu, bez úpravy Cockpitu — a jednu routu navíc zkoumanou v prohlížeči, protože scripts/cockpit-probe odvozuje své routy z běžícího serveru, ne ze seznamu. apps/majordomus-cli/tests/cockpit.rs toto tvrzení spouští: přidá pravidlo do jednorázového repozitáře a ověří, že schopnost dosáhne výpisu, vlastní stránky, prohlížeče objektů, vyhledávání a výpisu, který čte paleta.
Náklady jsou skutečné a pojmenované. Repozitář nyní vendoruje JavaScript třetí strany, což je nové: jeden soubor commitnutý a tři sestavené, všechny připnuté v package.json, všechny obsluhované z tohoto originu, žádný z nich povinný. Stylesheet je generovaný artefakt, k jehož regeneraci je potřeba Node, takže scripts/cockpit-assets --check je kontrola, kterou checkout bez Node nespustí — selže hlasitě, ne projde potichu. A stránka je další čtenář výstupů schopností, takže změna tvaru výstupu, kterou projekce snášela, může být nyní vidět na stránce; to je smysl, ale je to změna dosahu škod.
Co se tu nerozhoduje: zda Cockpit někdy zapisuje. Každá schopnost, ke které se dostane, je dotaz nebo příkaz nad pamětí tohoto procesu a nic v něm nezapisuje do repozitáře. Schopnost, která by zapisovala, by potřebovala vlastní rozhodnutí.