69. Schopnost smí být skriptovaná a automatizace repozitáře jsou skriptované schopnosti, ne shell
Kontext
Automatizace repozitáře žije v shellu: 122 souborů pod scripts/ (80 z nich shell, 43 ze 44 v scripts/ci/), 45 pod lib/ (38 shell), čtyři pod bin/ a 216 shellových testovacích případů. Každá opakující se třída vad, kterou si tento repozitář o sobě zaznamenal, má shellovou instanci: příznak BSD/GNU, který se na runneru chová jinak, pipeline, jejíž počet se ztratí v subshellu, kontrola, která nepřečte vlastní vzor a hlásí čisto, řazení, jehož pořadí závisí na locale stroje, soubor, který odvození neviděla, protože ještě nebyl v indexu gitu. Spustitelný soubor v Rustu mezitím už vlastní jeden registr, z něhož se odvozují příkazová řádka, API HTTP, OpenAPI, MCP a Cockpit.
Vlastník repozitáře 2026-09-15 rozhodl, že shell se má postupně nahrazovat Rhai, skriptovacím jazykem vloženým do Rustu, a že Perl nepřichází nikdy v úvahu. Tento záznam říká, čím se to stane ve slovníku tohoto repozitáře, protože zjevné čtení — „automatizační subsystém s automatizačním registrem“ — je druhý registr vedle toho, který slovník už má, a project.no-new-nouns ho odmítá.
Rozhodnutí
Schopnost může být skriptovaná. Nic dalšího se nepřidává.
docs/CONCEPTS.md už říká, že registr schopností „indexes the tool's own surface … and is derived from the layer and the code on every start“ a že schopnost, která průběžně hlásí, zavolaná jednou, je provedení. Skriptovaná schopnost je schopnost deklarovaná v souboru vrstvy místo v kódu Rustu, jejíž handler je skript Rhai místo zkompilované funkce. Index ji objeví jako každý jiný objekt vrstvy, stane se schopností v jednom registru a odtud dosáhne každého povrchu existujícími projekcemi — příkazová řádka, trasa HTTP, operace OpenAPI, nástroj MCP, generovaný formulář spouštěče v Cockpitu, generovaná reference. Přidání jedné ji nikde neregistruje.
Definice je druh. Nový druh v share/kinds.yaml s verzovaným schématem pod share/schemas/majordomus/ deklaruje skriptovanou schopnost: její id, název a popis, vstup a výstup jako JSON Schema, její efekt (read, process state, repository mutation), zda ji lze zrušit, a skript vedle ní. Skriptované schopnosti samotného repozitáře žijí pod .ai/repo/; ty, které distribuce dodává, žijí pod share/ a cestují v archivu release beze změny. Identita, validace neznámých klíčů, URI majordomus:// a zdroj MCP pocházejí z indexu, ne z vlastního loaderu.
Infrastruktura zůstává typovaný Rust. Skript se k repozitáři dostane jen přes schopnosti, jednou hostitelskou funkcí, která volá Context::execute — takže validace schématu, redakce, cache, čítače a rovina provádění platí pro každé volání skriptu přesně tak, jako platí pro volání z HTTP nebo MCP. Kde skript potřebuje něco, co žádná schopnost nenabízí (git status stromu, glob nad sledovanými soubory, spuštění programu s argumenty), odpovědí je typovaná schopnost v Rustu, ne větší skript. Žádná funkce nebere řetězec shellového příkazu. Program se spouští jménem se seznamem argumentů, pracovním adresářem, deltou prostředí a mezí.
Co smí skript dělat, je deklarováno a vynuceno. Skriptovaná schopnost deklaruje svůj efekt a hostitel za běhu odmítne volání jakékoli schopnosti, jejíž efekt jej přesahuje. Strop expozice, který dnes existuje jen v grafu příkazů, se přesouvá do CapabilityRegistry::build, kde drží každou schopnost — zkompilovanou i skriptovanou — pod stejným pravidlem: efekt nad stropem se nepromítá do HTTP ani MCP. Tím se uzavírá i mezera nalezená při psaní tohoto záznamu: plan.transition a recover.orphans zapisují do repozitáře a dnes se servírují přes obojí, zatímco jejich vlastní komentáře říkají, že je strop vylučuje.
Interpret je sandbox a není to eval. project.no-network-no-eval zakazuje eval, protože řetězec sestavený za běhu a spuštěný s plnou autoritou nástroje je program, který nikdo neprověřil. Skriptovaná schopnost je v každém ohledu opakem: skript je sledovaný soubor, prověřovaný jako každý jiný; kompiluje se jednou z toho souboru, nikdy z řetězce dodaného volajícím; vlastní funkce eval Rhai je zakázána; engine nemá vlastní přístup k souborům, procesům ani síti a ke světu se dostane jen přes schopnosti, jejichž efekty deklaroval; a engine vynucuje limity na operace, hloubku volání, hloubku výrazu a velikosti řetězců, polí a map, se zrušením a termínem kontrolovanými při každé operaci. SECURITY.md to uvádí jako deklarovanou výjimku v termínech samotného pravidla a sken pravidla je naučen rozlišit soubor skriptu od volání eval.
Konfigurace. rhai 1.26 s sync (sdílený server spouští handlery na vláknech), serde (typované hodnoty překračují hranici přes schémata, která je už popisují), metadata (hostitelské funkce, které smí skript volat, jsou introspektovatelné) a no_time (čas se ke skriptu dostává přes jeho kontext, nikdy skrytými hodinami). unchecked, internals a debugging zůstávají vypnuty. Moduly se rozkládají jen mezi objevenými skripty, nikdy z celého souborového systému.
Zamítnuté alternativy
Automatizační subsystém s vlastním registrem, runtime a API. Byl by to druhý registr, který project.no-new-nouns zakazuje, a každý povrch by se ho musel naučit; stávající registr už se promítá do všech.
Doslovný přepis každého shellového skriptu do Rhai. Většina skriptů je doménová logika — parsování YAML, klasifikace stavu gitu, procházení registru — která patří do typovaného Rustu. Soubor Rhai, který znovu implementuje parsování git status, je stejná vada v jiné syntaxi.
Obecná schopnost shell nebo exec. Udělala by ze skládání řetězců příkazů nejsnazší cestu a za každý povrch by postavila nerecenzovaný program. Pokud by se kompatibilní únik někdy nedal vyhnout, je to samostatně pojmenovaná schopnost s nejvyšším možným efektem, nikdy neprojektovaná mimo příkazový řádek, a každé její použití se hlásí.
Perl, Python nebo Node jako přenosná vrstva. Každý je runtime, který by uživatel musel nainstalovat; Rust executable je již nainstalován všude, kde je Majordomus.
Důsledky
- Shellový nástroj se zmenšuje migrací, ne přepisem: každý skript se klasifikuje — doménová logika do schopnosti v Rustu, orchestrace do skriptované schopnosti, nevyhnutelný bootstrap se ponechá s deklarovanou výjimkou, mrtvý kód se smaže — a kontrola odmítne nový shell pod řízenými cestami bez položky ve strojově čitelném seznamu výjimek.
- Skriptované schopnosti se posuzují jako ty kompilované: definice platná podle schématu, test, případy měření výkonu, generovaná dokumentace a kontrola pokrytí Rustu, který je spouští.
- Diagnostika získává pozici ve zdroji jednou, ve sdíleném typu diagnostiky, takže chyba skriptu pojmenuje soubor, řádek a sloupec na každém povrchu.
- Uživatel spouští automatizaci repozitáře samotným nainstalovaným spustitelným souborem: bez Bash, bez toolchainu Rust, bez samostatné instalace Rhai.
- Práce přistává jako řezy milníku
automation, každý mergnutý samostatně.