27. Příkaz se deklaruje jednou a každý povrch je projekcí grafu příkazů
Kontext
Tři programy odpovídají člověku v tomto repozitáři: spustitelný soubor v Rustu, shellový nástroj nesoucí životní cyklus úlohy a runner workflow. Každý deklaruje své vlastní příkazy a každá deklarace už je autorita — strom clap, share/commands.yaml sladěný s dispatch tabulkou nástroje a vlastní strukturovaný dump runneru.
Každý povrch, který chtěl tyto příkazy ukázat, držel seznam místo jejich čtení. justfile nesl recept na příkaz s popisem zkopírovaným z nápovědy příkazu. Web parsoval zprávu nápovědy. Shell completion, kdyby existoval, by nesl čtvrtou kopii a Cockpit pátou.
Důkaz, že tohle se rozpadá, je v repozitáři, ne v argumentu. Schopnost deklarovala, že je dosažitelná jako majordomus scope classify; příkazová řádka takový příkaz nikdy neměla a generovaná reference jej přesto dokumentovala. Nic si toho nemohlo všimnout, protože nic obě deklarace nespojovalo. docs/HARDCODING_LEDGER.yaml už obecný případ — veřejnou plochu příkazů bez vlastníka — pojmenoval jako zjištění priority 1.
Otázkou nebylo, zda mít jednoho vlastníka. Bylo, kde vlastník sídlí, když tři programy legitimně deklarují příkazy a žádný z nich by neměl přestat.
Rozhodnutí
Skládat, ne centralizovat. Graf příkazů čte tři deklarace, které už existují, a vytváří jeden typovaný dokument; každý povrch je jeho projekcí a žádný nevede katalog.
- Identita se odvozuje:
<program>.<path>—executable.worktree.status,tool.check,workflow.site-build. Program je součástí identity, protože dva ze tří sdílejí na cestě stejné jméno. Nic nezapisuje identitu ručně. - Sémantika, kterou deklarace nenese, se anotuje jednou, vedle ní, v
command_graph/semantics.rs: co spuštění příkazu mění, zda drží terminál, co vyžaduje, odkud pocházejí hodnoty argumentu a názvy receptů, které předcházejí mostu. Výchozí je to, čím je většina příkazů, takže se zapisuje jen odchylka, a úplnost se vynucuje oběma směry. - Vystavení se odvozuje, nikdy nedeklaruje: model efektů (read-only, lokální mutace, mutace repozitáře, síťová mutace, destruktivní) a model interaktivity rozhodují v jedné funkci, který povrch nese příkaz. Každý povrch, který příkaz zadržuje, nese důvod.
- Most workflow je generovaný a nesledovaný, pod
.ai/local/cache/. Je čistou funkcí stromu, v němž sedí, má otisk a vstup do repozitáře jej obnoví, když se změnila deklarace za ním. - Jeden dokončovací engine odpovídá na každém povrchu.
just <TAB>přeloží jméno receptu zpět na kanonický příkaz a pak mu odpovídá stejný kód ze stejných metadat argumentů jakomajordomus <TAB>.
Zamítnuté alternativy
Ručně udržovaný commands.yaml pro každý program. Vlastní navržená náprava ledgeru pro shellový nástroj a pro něj správná: ten registr uvádí referenční sémantiku, kterou shellová dispatch tabulka nevyjádří. Rozšířit ho na spustitelný soubor by znamenalo opakovat celý strom clapu — každý argument, výchozí hodnotu i množinu hodnot — v YAML a ručně je držet v souladu. Příkazy spustitelného souboru už deklaraci mají; druhá je vada, ne náprava.
Odvození grafu z workflow runneru. just --dump popisuje každý recept a graf z něj čtený by nepotřeboval žádný nový kód. Učinil by ale z runneru autoritu pro to, co ty dva programy nabízejí, což je přesně naopak: recept je projekce příkazu a projekce, která krmí kanonický zdroj, činí ze zdroje vykreslení jeho vlastního výstupu.
Generovat most do stromu. Sledované generované soubory jsou běžným vzorem repozitáře a tento by byl vidět v revizi. Musel by se také znovu generovat každým, kdo změní příkaz, kontrolovat kontrolou a ručně slučovat na každé větvi, která se dotkne příkazové řádky — pro soubor, který je čistou funkcí stromu. Cena zastarání je porovnání hashe; cena merge není.
Nechat jeden program vyhrát kolizi názvu receptu. majordomus bench existuje v obou programech. Výběr jednoho podle pořadí původu by učinil význam just bench závislým na tom, který přispěvatel náhodou běžel první. Místo toho jsou oba kvalifikovány a krátký zápis pro kterýkoli z nich je rozhodnutí deklarované jednou jako alias.
Důsledky
Přidání příkazu do kterékoli ze tří deklarací ho dostane do příkazové řádky, mostu workflow, shellového dokončování obou povrchů, strojových povrchů, které politika připouští, Cockpitu a generované reference. Neexistuje druhá registrace, na kterou by se dalo zapomenout.
Graf odmítne promítat, dokud nese chybu, a kontrola command-graph odmítne změnu: duplicitní identitu, název receptu, který by převzaly dva příkazy, schopnost, která si nárokuje neexistující příkazovou řádku, anotaci nebo alias, který jmenuje neexistující příkaz, nebo tajný argument, který by něco vyjmenovalo.
Tři náklady jsou nyní skutečné a trvalé. Tabulka anotací vedle příkazové řádky musí zůstat úplná — když není, build selže, což je smyslem. Politika vystavení je jediná funkce, které se podřizuje každý povrch, takže povrch, který výjimku skutečně potřebuje, ji musí obhájit na jednom místě, místo aby ji tiše implementoval. A názvy mostu jsou algoritmus, takže se název receptu příkazu změní, když se změní jeho cesta; aliasy kompatibility existují proto, aby to bylo přežitelné, a jsou deklarovány jednou.
bin/majordomus drží share/commands.yaml. Není to duplikát dispečerské tabulky: uvádí referenční sémantiku — třídu, fázi, co příkaz čte a zapisuje, jeho návratové kódy — kterou shell deklarovat neumí, a doktrína command_surface_complete obojí sladí.