43. Vstup do repozitáře zvedne jeho runtime, jedním bootstrapovým příkazem, který volá vstupní soubor
Kontext
ADR 0035 a slice, který jej implementoval, způsobily, že vstup konverguje pro agenta: startovní událost providera otevře epizodu a zavolá serve ensure, takže worker přicházející přes Claude Code, Codex nebo Gemini najde sdílený server repozitáře už obsluhující jeho checkout. docs/ENTRY.md je stránka pro operátora a project.entry-converges je pravidlo.
Totéž pravidlo záměrně uzavřelo druhou polovinu. „Vstup shellem hlásí a neobsluhuje“: soubor, který shell vyhodnotí při vstupu do adresáře, nesmí spustit server, protože ADR 0003 říká, že není proces bez klienta a shell není klient. scripts/ci/entry-converges to vynucuje tvarem — podpříkaz serve nebo mcp kdekoli v .envrc nebo bin/majordomus-env je nález — a test/cases/118 odmítnutí dokazuje tím, že přesně takový řádek podstrčí.
Požadavek operátora je, aby tento systém fungoval bez toho, aby si člověk nebo agent pamatoval posloupnost příkazů, a proti tomuto požadavku je uzavřená polovina vadou. Co dnes člověk vstupující do tohoto repozitáře v terminálu dostane, je banner, PATH, most workflow a, pokud žádný nikdo nespustil, žádný runtime: žádný Cockpit, žádné HTTP API, žádný Swagger, žádnou nástěnku peerů — tedy žádný způsob, jak by ostatní pracovníci na tomto stroji viděli, že nejsou sami, což je selhání, kterému má project.work-is-claimed-before-it-is-built zabránit a které tento repozitář zaplatil víckrát. Lékem byl příkaz, který je třeba si pamatovat, a to je přesně to, proti čemu návrh stojí.
Než se o tom dalo rozhodnout, bylo nutné rozlišit dvě věci, protože staré pravidlo je smíchalo.
Námitka, kterou ADR 0003 skutečně vznáší, se týká procesu bez vlastníka, ne toho, kdo napsal cd. „Žádný proces bez klienta“ je výrok o serveru, který přežije důvod své existence. Byla zodpovězena v čase, a ne v každém okamžiku, když serve ensure dostal DEFAULT_IDLE_SECONDS: server, ke kterému se nikdo nepřipojí, sám skončí. Shell, který vstoupí do repozitáře, přečte jeho prostředí a odejde, je klientem přesně tak — a přesně tak málo — jako agent, jehož relaci poskytovatel otevřel a zavřel. Obojí pokrývá stejný klidový život a ani jedno nezanechá proces.
Námitka proti shellovým rourám v .envrc se týká souboru, ne efektu. project.envrc-is-an-adapter má pravdu a platí dál: git rev-parse tady, curl támhle, pid vyčtené z lease pomocí sed jinde je druhá implementace něčeho, co spustitelný soubor už ví, běžící na horké cestě každého cd, a nic ji netestuje. Nic z toho se nemění, pokud vstupní soubor volá jeden příkaz, jehož celé chování je vlastněno, typováno, měřeno a testováno uvnitř spustitelného souboru.
Rozhodnutí
Vstup do repozitáře je jedno volání a toto volání zvedne runtime.
.envrcvyhodnocuje přesně jeden příkaz nástroje,majordomus env enter, a neobsahuje nic jiného nežPATH_addawatch_file. Nenese žádný git, žádnégrep,sed,awkanijq, žádný pid, žádný port, žádnécurl, žádné řízení toku a žádné obchodní pravidlo. To, že nyní může způsobit existenci serveru, nic z toho nepovoluje;project.envrc-is-an-adaptersi ponechává svůj seznam slov a získává pozitivní polovinu — jedno volání nástroje, a to bootstrapové.- Spustitelný soubor vlastní celý vstup: prostředí, které exportuje, banner, který kreslí, most workflow, který obnoví, když se pohnula deklarace za ním, a runtime, který zajistí.
env enterčte politiku jednou, z tohoto čtení rozliší snímek a konverguje na server toutéž funkcíconverge, kterou voláserve ensure— jedna implementace, dva povrchy, podleproject.interfaces-are-projections. - Vstup nikdy nečeká na připravenost. Studený vstup přečte lease, zjistí, že nic neodpovídá, spustí server jako vlastní proces a vrátí se. Server se stane připraveným vedle shellu, ne před ním, a
watch_filenad lease je to, co o okamžik později přivede publikovanou adresu do prostředí.--waitexistuje pro volajícího, který chce opačné chování; vstupní soubor jej nepředává. - Vstup nikdy nesestavuje a nikdy neprovádí ensure ze zastaralého spustitelného souboru. Chybějící spustitelný soubor je jeden řádek na standardním chybovém výstupu a exit 0, jako dříve. Soubor starší než jeho zdroje je tentýž řádek a žádný runtime: adaptér nastaví
MAJORDOMUS_RUNTIME=off, protože server spuštěný ze zastaralého kódu odpovídá stromem, který už neexistuje — Cockpitu, API a každému dalšímu připojenému pracovníkovi, z nichž nikdo nevidí, že proces je starší než checkout, který tvrdí, že servíruje. Je to odmítnutí, kterélib/capture.shuž činí na cestě agenta, rozhodnuté stejnýmmj_rust_stale, takže checkout, kde se dvě vstupní cesty rozcházejí, nemůže existovat. - Vstup nikdy nesmí selhat. Každý výsledek za běhu je nejvýše jeden řádek na standardním chybovém výstupu a exit 0. Nenulový exit zde způsobí, že direnv ohlásí selhání celého prostředí a nechá člověka s rozbitým shellem v repozitáři, který je v pořádku.
- Jeden přepínač, obě cesty vstupu.
session.ensure_server_on_startje odpověď repozitáře na „přivede vstup sem runtime nahoru“ a nyní řídí shell stejně jako událost startu. Nepřidává se druhý klíč: repozitář, který na tu otázku odpovídá dvakrát, je repozitář, kde se obě odpovědi rozejdou.MAJORDOMUS_RUNTIME=offje totéž odmítnutí pro shell jednoho člověka, ne pro repozitář. - Vstup má rozpočet a je to klíč politiky jako každý jiný.
benchmark.budget.enter_msabenchmark.budget.enter_cold_msjsou deklarovány v politice, kostře, allow listu a schématu atest/cases/190měří obojí a selže nad kterýmkoli. Vstup do adresáře je něco, co člověk udělá desítkykrát denně a nikdy se nerozhodl na to čekat.
Co je nahrazeno. Klauzule project.entry-converges, která zní „vstup přes shell hlásí a neobsluhuje“, a kontrola, která odmítala podpříkaz serveru kdekoli na vstupní cestě. Obojí se mění na: vstupní soubor sám nic nespouští — žádný builder, žádnou úlohu na pozadí, žádný serve, žádný mcp — a provede přesně jedno volání bootstrap příkazu, kde se rozhoduje o spuštění čehokoli. Pravidlo jde na verzi 2 a stejně tak project.envrc-is-an-adapter.
Důsledky
Dobré. Požadavek je splněn bez čehokoli, co by si člověk musel pamatovat: terminál v tomto repozitáři má do vteřiny po cd Cockpit, API a nástěnku peerů a druhý pracovník na tomto stroji vidí prvního. Obě vstupní cesty spouštějí jednu funkci, takže agent i člověk skončí u téhož serveru, ne u dvou čtení téže otázky. Odmítnutí zastaralého spustitelného souboru nyní pokrývá cestu, která o něm neměla názor. Naměřená cena runtime poloviny za tepla je v šumu toho, co vstup stál už dřív: při load average 100 běžel teplý vstup 166–241 ms proti 166–287 ms u starého vstupu jen s exportem.
Nalezeno při budování a opraveno zde. Server spuštěný Command::spawn zdědí každý deskriptor souboru nad třemi přesměrovanými. direnv předá souboru, který vyhodnocuje, vlastní extra deskriptor a nevrátí se, dokud se ta roura nezavře, takže první skutečné cd do checkoutu po vstupu, které začalo spouštět server, viselo 481 sekund a uvolnilo se, až když byl server zabit ručně. Žádný test v tomto repozitáři to nemohl vidět: případ, který zachytává výstup v command substitution, nepředá příkazu žádný deskriptor nad dvě, což je přesně to, co direnv nedělá. spawn_server je nyní zavírá mezi fork a exec, test/cases/190 jeden záměrně otevře a vyžaduje, aby byl po návratu vstupu zavřen, a totéž cd přes skutečný direnv nyní stojí 917 ms za studena. Třída si zaslouží jméno: horká cesta dokázaná jen přes harness je dokázaná za podmínek harnessu a otevřené deskriptory volajícího jsou jednou z podmínek, které harness tiše normalizuje.
Špatné, a přijaté. cd nyní může spustit proces. To je celé rozhodnutí a hranicí je nečinný život: server, ke kterému se nikdo nepřipojí, skončí. Deset shellů, které vstoupí do studeného checkoutu ve stejný okamžik, může každý spustit server dřív, než kterýkoli zapsal lease; volba — jedno vytvoření souboru lease přes O_EXCL — nechá jeden z nich sloužit a ostatní skončí s 0 během milisekund, takže výsledkem je jeden autoritativní server a několik procesů, které žily okamžik. test/cases/191 to dokládá se skutečnými souběžnými procesy pro deset současných vstupů, mrtvý pid v lease, obsazený port, server, který spadne při startu, a dva worktrees najednou.
Sledovat. Položku, která získá druhý důvod být pomalá. Všimne si toho rozpočet, a je to klíč politiky, aby jeho zvýšení byla změna, kterou recenzent vidí.
Zvažované alternativy
Nechat to na startovní události poskytovatele. To je to, co existuje, a slouží to jen agentům. Člověk v terminálu — a obecný klient MCP, který nikdy nesourcuje .envrc — měl stále příkaz, který si musel pamatovat.
Nechat .envrc volat serve ensure přímo. Jeden řádek, a je to řádek, proti kterému bylo staré pravidlo napsáno — z dobrého důvodu, který toto rozhodnutí přežívá: dává rozhodnutí do vstupního souboru. Zda zajistit, s jakou dobou nečinnosti, na kterém portu, kdy mlčet, kdy odmítnout, protože je spustitelný soubor zastaralý — to vše by pak žilo v shell souboru, který nic netestuje. Příkaz bootstrap existuje právě proto, aby vstupní soubor nenesl nic z toho.
Čekat na připravenost při studeném vstupu. Adresa by byla k dispozici v prvním shellu místo ve druhém a první cd do repozitáře by byl čekáním na sekundy. watch_file tuto mezeru už uzavírá, aniž by kdokoli čekal.
Druhý klíč politiky pro cestu shellu. Zamítnuto: jedna otázka, jeden přepínač. Viz výše.