Přeskočit na obsah

Tato stránka byla automaticky přeložena z angličtiny, jediného jazyka, ve kterém se tento web píše. Číst originál.

Rozhraní MCP

povrch MCP pouze pro čtení u Rust executable: co poskytuje, co o tom rozhoduje, jak selhává a co odmítá poskytnout

Vykresleno z docs/MCP.md — stejného Markdownu, který ukazuje GitHub.

Co spustitelný soubor v Rustu pod apps/majordomus-cli/ poskytuje klientovi MCP, odkud to pochází a co odmítá. Chování tak, jak je implementováno a testováno; kde se implementace a tento dokument rozcházejí, je chybný dokument a mění se ve stejném commitu jako oprava. Vývojářský detail (architektura, každá volba, schéma druhu) je ve vlastním README.md aplikace.

Co to je

Proces, který klient spouští a který mluví Model Context Protocolem na svém stdin a stdout, obsluhuje AI vrstvu repozitáře (čtení a pojmenované příkazy, které mění stav) a připojuje se k jedinému sdílenému serveru repozitáře: první takový proces v repozitáři naváže loopback HTTP projekci vedle své stdio relace (domovská stránka s výpisem všech povrchů, dokumentace tohoto repozitáře, Swagger UI, dokument OpenAPI, každá routa schopnosti, MCP přes HTTP) a oznámí kde; každý další se k ní připojí.

cargo build --manifest-path apps/majordomus-cli/Cargo.toml
apps/majordomus-cli/target/debug/majordomus mcp --inspect    # what would be served, and every diagnostic
apps/majordomus-cli/target/debug/majordomus mcp              # serve until the client goes; the first one is the server
apps/majordomus-cli/target/debug/majordomus mcp --standalone # this client alone: no port, no lease, no peers
bin/majordomus-mcp                                           # the same, built first when needed: what a client configuration names

Log na stderr to pojmenuje v okamžiku, kdy server běží:

shared server listening on http://127.0.0.1:8741 — 7 surface(s): api http://127.0.0.1:8741/api/v1, cockpit http://127.0.0.1:8741/cockpit, docs http://127.0.0.1:8741/docs, home http://127.0.0.1:8741/, mcp http://127.0.0.1:8741/mcp, openapi http://127.0.0.1:8741/openapi.json, swagger http://127.0.0.1:8741/swagger; the one server of this checkout ...

Není to démon: nic jej nespouští kromě klienta, nic jej nedrží naživu kromě klientů a končí, když zmizí jeho vlastní klient a odejde poslední připojený. Nedrží žádný stav kromě paměti, nepotřebuje databázi a zapisuje jeden soubor: lease pod .ai/local/state/mcp/, lokální polovinou checkoutu, kterou vrstva vyhrazuje pro provozní stav, odstraněný při zastavení serveru. Je to jedna projekce registru schopností spustitelného souboru (CAPABILITIES.md): stejné schopnosti jsou trasy HTTP a příkazy capabilities a každý nástroj a zdroj zde je odvozen ze záznamu registru, žádný není deklarován v kódu MCP. Rozhodnutí je .ai/repo/adrs/0003-shared-mcp-server-peers-and-client-autostart.md.

Jeden server na checkout

volbaprvní proces, který (atomicky) vytvoří .ai/local/state/mcp/server.json, je server; naváže se, zapíše do souboru svou URL a zaloguje ji
port--http-port na --http-host (výchozí: rozhraní, které MAJORDOMUS_HTTP_HOST na tomto stroji jmenuje, a 127.0.0.1, když žádné nejmenuje — níže). Bez --http-port server žádá o port, který si jeho checkout odvodí, při každém startu stejný: 8741 pro primární checkout repozitáře a pro propojený worktree port v rozsahu 8742–9740 odvozený z identity worktree (cli::checkout_port), takže dva checkouty jednoho repozitáře žádají o různé porty a záložka s adresou nebo konfigurace klienta Streamable HTTP přežije restart (I2702). serve, serve ensure a env enter ho odvozují stejně a server.status ho hlásí jako požadovaný port. Obsazený port se nahradí volným a obojí se zaloguje, takže druhý repozitář ani zatoulaný proces nikdy nezabrání klientovi ve startu
připojovánípozdější majordomus mcp přečte pronájem, ověří, že server odpovídá za tento kořen, a přemostí svůj stdio na /mcp: jeden HTTP požadavek na zprávu, ping každých dvacet sekund, žádný index ani vlastní registr, takže startuje v milisekundách
zastaralý leaselease, jehož server na tento kořen neodpovídá (proces byl zabit), soubor, který není dokumentem lease, prázdný soubor, nebo lease, jehož vlastník nepublikoval žádné URL během bind grace, převezme další proces a log říká, o který z těchto případů šlo; nic, co klient zanechá, nemůže zamknout ostatní
životnostserver slouží, dokud je připojen jeho vlastní klient nebo kterýkoli peer; když odejde klient vlastníka první, log řekne serving until the last peer leaves; když odejde poslední peer, server se zastaví, zavře port a odstraní zápůjčku
čerstvostserver sleduje repozitář, který obsluhuje: dříve než odpoví na požadavek, porovná otisk řídicích souborů gitu (HEAD, staging index, packed-refs, reflog, značky merge a rebase — po jednom stat) s tím, při kterém byl postaven jeho aktuální odečet, a požadavek, který zjistí rozdíl, přestaví vrstvu. Commit provedený za běhu serveru je viditelný přes API, přes MCP i v Cockpitu bez restartu; klient připojený před commitem si ponechá relaci i místo na nástěnce peerů. Žádný poll, žádné vlákno, žádný watcher. project.the-server-sees-the-current-tree
signálySIGTERM, SIGINT nebo SIGHUP (serve stop, klient zabíjející svůj server, Ctrl-C v terminálu) požádá server o řádné zastavení: handler jen zaznamená žádost a server zavře své relace a epizody, zavře své listenery a uvolní lease — jen dokud je soubor ještě jeho — než skončí. serve vrací 0; majordomus mcp obsluhující vlastního klienta pak zemře na signál, protože stdio relace jeho klienta byla přerušena. Druhý signál nebo řádné zastavení, které do osmi sekund neskončilo, ukončí proces okamžitě; kill -9 nelze zachytit a další proces převezme zastaralý lease
převzetípřemostěný peer, jehož server zemřel, volí znovu při další zprávě: sám se stane serverem a přenese initialize svého klienta, takže si toho klient nevšimne, nebo se připojí k procesu, který zvítězil první (re-attached to the shared server); když nemůže sloužit ani jedním způsobem (jeho vlastní --strict odmítá degradovanou vrstvu), klient dostane chybu JSON-RPC, která říká proč, nikdy ticho. Zpráva, která se potkala se selháním, se pošle znovu, jen když server prokazatelně neběžel — spojení nikdy nevzniklo nebo server odpověděl 404 (relace ztracena) či 409 (lease ztraceno) — nebo když je idempotentní: každá metoda kromě tools/call a nástroj, jehož deklarované nápovědy provádění to říkají (každé čtení). Volání, které bylo odesláno a nikdy nedostalo odpověď (timeout, přerušené spojení, 5xx), se znovu neposílá: klient dostane -32603 s textem outcome unknown a id požadavku, takže zapisující nástroj nikdy neběží dvakrát
odmítnutípřemostěný peer, jehož server obsluhuje a nepřijme požadavek — požadavek odeslaný před initialize je zodpovězen 400 session_required, zpráva nad limit těla 1 MiB 413 — předá toto odmítnutí klientovi jako chybu JSON-RPC (-32600) jmenující stav a nikoho nevolí; zpráva nad limit je zodpovězena payload too large, aniž by byla odeslána. Volba je pro server, který nelze zastihnout, který požadavek přijal a nikdy neodpověděl, který ztratil relaci a znovu ji neotevře (404), nebo který říká, že ztratil lease (409 lease_lost)
mezezpráva má na obou transportech nejvýše 1 MiB — řádek stdin nad ním se odpoví chybou parsování a relace pokračuje, tělo požadavku nad ním se odpoví 413; odpověď, kterou čte most, registrace rendezvous nebo spojení mesh, má nejvýše 32 MiB; požadavek jmenující MCP-Protocol-Version, kterému tento server nerozumí, se odpoví 400; id relace má 128 bitů ze zdroje náhodnosti operačního systému a relace se při zápisu své odpovědi znovu opatří razítkem, takže dlouhý požadavek nikdy nenajde svou relaci sklizenou za zády (I2147)
volby--discovery a --strict serveru platí pro každou relaci, kterou obsluhuje; můstek je zdědí a log říká, ke kterému serveru se připojil
--standalonechování první verze: jen tento klient, žádný port, žádný lease, žádní peeři, nic se nikam nezapisuje
degradovánokdyž nelze lease zapsat nebo nahradit, nebo se sdílený server nespustí, klient je obsloužen sám přesně tak, jako by to udělal --standalone, a log řekne cannot use the shared server s cestou a důvodem
servetentýž sdílený server bez vlastní stdio relace; pokud už nějaká běží, zaloguje URL a skončí s 0

Jeden repozitář, každý jeho server

Server obsluhuje checkout. Propojený worktree je vlastní checkout — má manifest, takže má kořen, lease i vlastní server — a do ADR 0035 nic neříkalo, že dva takové servery patří jednomu repozitáři. Nyní indexová trasa (GET /) jmenuje vedle identity samotného checkoutu i git repozitář, ke kterému checkout patří: commit je commit, ze kterého byl odpovídající spustitelný soubor sestaven, celý nebo unknown, a dirty zda jeho strom nesl necommitnuté změny (null, když to sestavení nevědělo) — stejná dvě pole odpovídají GET /api/v1/live a GET /api/v1/ready; stale je null, dokud je spustitelný soubor, ze kterého byl proces načten, stále souborem na disku, a říká proč, jakmile ho přestavba nahradila nebo odstranila — proces pak obsluhuje kód, který už neexistuje, a GET /api/v1/ready odpovídá ready: false se stejným důvodem (I1502); repository_id je checkoutu (otisk jeho kořene, s čím lease sonda porovnává), git_repository_id je repozitáře (otisk adresáře gitu, který sdílí každý worktree; chybí tam, kde se git nedá zeptat), linked_worktree říká, zda jde o primární checkout, a leaseholder říká, zda odpovídající proces je stále tentýž (níže). branch a head jsou větev a commit, z nichž byly obsluhované odpovědi čteny, jak je četl index (I1031). Domovská stránka a shell Swaggeru na /swagger nesou stejnou identitu na jednom řádku vedle svého obsahu; nic z toho není cesta, protože kdo dosáhne socketu, se dozví, který checkout odpověděl, a nikdy kde leží. GET /api/v1/server — nástroj majordomus_server, zdroj majordomus://server — vypisuje každý checkout, který git pro repozitář eviduje, primární první, každý s jeho leasem, stavem jeho serveru a počtem peerů, které drží, a říká, kde stojí server tohoto checkoutu měřeno proti tomu, co by obsluhoval tento spustitelný soubor:

postavenívýznam
absentžádný lease: tento checkout nic neobsluhuje
startingpronájem bez adresy, dost mladý na to, aby ho jeho vlastník ještě navazoval
readyserver, který lease jmenuje, odpovídá za tento checkout, ze souboru na disku, ve verzi tohoto spustitelného souboru
outdatedodpovídá, ale z jiné verze, nebo ze souboru nahrazeného od jeho startu: vše, co říká, je včerejší
stalelease jmenuje server, který neodpovídá, nebo vůbec není leasem; důvod říká které

Seznam je to, co volající dostane, když se na nic neptá, a stojí čtení lease a sondu na checkout — na počítači se sty registrovaných worktree sto od každého. Volající, který chce vědět jen o checkoutu, ve kterém je, to řekne — checkouts=this v query stringu, ve vstupu nástroje nebo --checkouts this na příkazové řádce — a toto čtení nevyčísluje žádný jiný checkout, nečte žádný jiný lease a nesonduje žádný jiný server. Výchozí je celý seznam, protože to je odpověď, kterou tato schopnost dávala dříve, než pole existovalo.

Lease samotný je jeden typ, čtený jednou (lease::LeaseDocument, lease::LeaseFile::read): volba, serving jen pro čtení, snímek prostředí a stav ho všechny parsují stejným čtením a nese version serveru vedle spustitelného souboru, z něhož byl spuštěn. .ai/local/state/mcp/server.json je stále místem, kde ho člověk čte pomocí cat; stav je místem, kde ho čte program.

Server, který už není tím serverem

Lease lze převzít, i když proces, který ho držel, stále běží a je zdravý — protože jeho spustitelný soubor byl nahrazen rebuildem, což volba čte jako „servíruje kód, který už není na disku“, nebo protože je to starší release než volící klient. Instalátor ukládá každý release do vlastního adresáře, takže upgrade mění cestu spustitelného souboru a kontrola rebuildu to nikdy nevidí; kontrola releasu ano. Rozhoduje jediný verdikt (lease::version_verdict) pro volbu, serve ensure i server.status, řadící releasy podle precedence sémantického verzování definované release::version::Version, takže server na 1.0.0-alpha.1 je novější než 0.29.0 a starší než 1.0.0-alpha.2 a 1.0.0:

release serveru jeklient tohoto spustitelného souboru
totéžpřipojuje
staršípřebírá lease; starší server předá své relace dál, jak je uvedeno níže
novějšíse připojí a jeho odpověď initialize nejprve říká, že tento klient je starší
nepublikováno, nebo neřazeno jako release (není to sémantická verze, například lokální tag sestavení)připojí se; server.status hlásí outdated

Starší klient nikdy nepřevezme novější server, takže dva releasy vedle sebe nebojují o lease: drží ho novější (I2163). Nahrazený proces se nezabíjí. Drží svůj socket, svou nástěnku peerů a generaci vrstvy, kterou načetl, a obsluhuje relace, které už měl, dokud neskončí. Je to záměrné: klient uprostřed odpovědi nemá přijít o svůj server proto, že někdo spustil cargo build.

Co nesmí dělat, je dál být tím serverem. Od okamžiku, kdy jeho vlastní čtečka zjistí, že lease už nenese jeho token:

říká to jeho indexGET / odpovídá leaseholder: false; každé další pole — repository, repository_id, git_repository_id — je o něm stejně pravdivé jako o aktuálním serveru, což je přesně důvod, proč je třeba je jedním polem oddělit
sonda jej odmítnelease::probe klade tři otázky, ne dvě: server Majordomus, tento checkout, stále držitel leasu. Klient s adresou z doby před převzetím se dozví, že tam žádný server není, místo aby dostal nástěnku, kterou nikdo jiný nevidí
nepřebírá nikoho novéhoinitialize bez relace dostane 409 lease_lost a odkaz na spouštěč jako cestu k aktuálnímu serveru
opouští meshobjevování a kooperace se zastaví; tento checkout reprezentuje jen nástupce, takže vyřazený proces nemůže dál nahrazovat své ověřené odkazy
jeho otevřené relace se předají dálodpovídá se jim po deklarovanou lhůtu zaneprázdnění (server.busy_grace_seconds), takže rozpracovaný požadavek a ty za ním skončí tam, kde začaly; poté je každá relace uzavřena a dostane 404, most klienta otevře novou, je odmítnut s 409 lease_lost a zvolí aktuální server; když nezbude žádná relace, nahrazený proces skončí (I2127)

tests/mesh_lease_retirement.rs dokazuje převzetí se skutečnými servery: vyřazený proces stále odpovídá svému připojenému klientovi MCP, přestane se účastnit meshe a nástupce se synchronizuje se vzdáleným runtime bez opakovaných opětovných připojení.

Server příliš starý na to, aby odpověděl na třetí otázku, sonda přijme. Na tom endpointu jej nelze odlišit od aktuálního a jeho odmítnutí by bylo horším selháním: živý server považovaný za mrtvý se převezme, a tak se stane, že jeden checkout má dva.

Bylo to změřeno dřív, než to bylo napsáno: 2026-09-10 měl tento repozitář dva servery, jeden lease, a relace, jejíž prostředí nesla starší adresu, četla nástěnku peerů s jedním peerem, zatímco nástěnka, kterou sdíleli všichni ostatní, měla dva.

Čtyři významy slova „připraveno“

Tento spustitelný soubor odpovídá „ready“ čtyřikrát a audit, který podnítil ADR 0035, počítal tři z nich za nehodu (docs/ENTRY_AUDIT.md, příčina 5). Nejsou to jedna otázka špatně rozdělená: jsou to čtyři otázky o čtyřech subjektech a sloučení kterýchkoli dvou by ztratilo to jediné, co ten čtenář potřebuje. Každá je zde pojmenována se svým vlastníkem, aby další čtenář nemusel znovu objevovat, na kterou se dívá.

otázkaodpověďkdo se ptá
Je výstup producenta plochy na disku?http::Served::ready(surface_id) — adresář, do kterého producent zapisuje, v sobě má soubory; route, na kterou tento spustitelný soubor odpovídá, je vždy připravenaúvodní stránka (GET /), která povrch bez výstupu vykreslí jako not built místo servírování 404
Může tento proces odpovědět na požadavek?health.ready — GET /api/v1/ready: registr a index sestavený při startu, jak vrstva načetla, a zda spustitelný soubor, z něhož byla načtena, je stále na disku — nahrazený ji činí nepřipravenou, s důvodem v stale — a zda tento proces stále drží lease checkoutu: server, kterému lease převzal jiný proces, také není připraven. Každý důvod je vypsán v reasons jako code: words (lease_lost, stale a layer_degraded, což ho nečiní nepřipraveným: zhoršená vrstva se poskytuje tak, jak se četla, s diagnostikou). health.report nese mesh jako samostatnou dimenzi, posuzovanou tak, jak mesh doctor posuzuje runtime, a unknown v procesu, v němž žádný server neběží. Jen lokální inicializacesonda připravenosti hostingové platformy. Záměrně se nespojuje s ničím mimo tento proces: kontrola připravenosti, která sonduje závislost, shodí nasazení kvůli něčemu, co není tento proces
Přijímá něco spojení na adrese, kterou pronájem publikoval?environment::ServiceAvailability — jedno TCP spojení s pevným rozpočtem a bez překladu jmen (environment::probe::reachable)snapshot prostředí, který běží na shellovém promptu (majordomus env, .envrc) a nesmí utratit HTTP round trip ani sáhnout na DNS, aby řekl, co ví
Je to, co tam odpovídá, aktuální?ServerStanding — server.status, z lease::probe (identita tohoto checkoutu přes HTTP) a verze a spustitelný soubor, které lease nesekaždý, kdo musí důvěřovat tomu, co server říká: serve ensure, serve stop, kontrola server příkazu health.report a briefing relace

Čtěte sloupec shora dolů a žebříček je jasný: třetí se ptá, zda je socket otevřený, čtvrtý, zda proces za ním patří tomuto checkoutu, ze souboru na disku, v tomto buildu. Zastaralý server odpoví na třetí a selže na čtvrtém, což je přesně třída, která tento repozitář už stála den.

health.report nese čtvrtý a jen čtvrtý. První je pro každý povrch zvlášť a patří na stránku, která povrchy vykresluje; druhý je výrok o procesu, který report vyřizuje, a ten nemůže být nepravdivý tam, kde report vzniká; a třetí nerozezná živý server od socketu, který drží někdo jiný. Kontrola deleguje na capability::builtin::server::standing_at — stejné čtení, ze kterého odpovídá server.status — takže report o zdraví a stav nemohou říct dvě různá slova o jednom leasu.

Zajištění serveru a jeho zastavení

majordomus serve status [--checkouts this|repository] [--format json]
                                            where this checkout's server stands, and every server of the repository
majordomus serve ensure [--idle S] [--wait S] [--port P]
                                            a ready server for this checkout, started if it must be
majordomus serve stop [--wait S]            end the server this checkout's lease names

serve ensure přečte lease a vyzkouší server, který jmenuje, přesně jako volba, a konverguje: ready se vytiskne a nic se nespustí; na starting se čeká; absent a stale spustí server jako vlastní proces — tento spustitelný soubor, serve --fallback --idle S, jeho log na .ai/local/state/mcp/server.log, ve vlastní skupině procesů, aby přežil shell, který o něj požádal — a čekají, až je připraven; outdated spustí server jen tehdy, když by volba lease převzala (tentýž spustitelný soubor, vyměněný na disku, nebo starší release než tento spustitelný soubor) a jinak se hlásí s nápravou, protože server jiného buildu, který odpovídá, tento příkaz nekončí. Spuštěno dvakrát nespustí podruhé nic; spuštěno třemi shelly najednou nechá volba jeden ze tří serverů navázat a ostatní ustoupí. Volání je omezeno --wait; server, který se včas nepřipravil, se hlásí se stavem, jehož dosáhl, a exit kódem 10.

Server, který ensure spustí, nemá vlastního klienta. Skončí, když ho --idle sekund (ve výchozím stavu patnáct minut) nic nepoužilo — není připojena žádná relace MCP, nebyl zodpovězen žádný požadavek HTTP a není otevřen žádný živý kanál /events, takže člověk, který čte Cockpit nebo volá REST API, si server, který používá, ponechá (I2131) — což zachovává linii ADR 0003 — žádný proces bez klienta — platnou v čase, ne v každém okamžiku: vstup agenta má server dlužen před prvním připojením a checkout, ve kterém nikdo nepracuje, žádný nedrží.

serve stop pošle signál serveru, který jmenuje lease, pokud tento server odpovídá za tento checkout, a čeká, až lease toho serveru zmizí — dokument, který přečetl, podle jeho tokenu, nejen cesta — a pak až proces skončí. SIGTERM, který pošle, je uspořádané zastavení, ne kill: server dá připojeným peerům dvě sekundy na odchod, zavře jejich relace a epizody, které nesly, zavře své listenery (rozpracovaná odpověď se dokončí první), uvolní svůj lease a vyprázdní mesh. Soubor odstraní jen proces, jehož token soubor stále nese: každé odstranění a každé přepsání lease ho znovu čte pod zámkem vedle něj (.ai/local/state/mcp/server.lock), takže server zastavený těsně poté, co byl jeho lease převzat, nechá lease svého nástupce na pokoji. serve pak skončí s exit 0 vlastním návratem; zastavení, které nedoběhne do osmi sekund, nebo druhý SIGTERM ho přesto ukončí. serve ensure stále čekající ve volbě převezme uvolněnou cestu během milisekund, takže čekání na cestu by hlásilo server, který se nezastavil, ačkoli už se zastavil; převzetí se místo toho pojmenuje v odpovědi. Lease, který jmenuje server jiného checkoutu, nebo takový, který neodpovídá, se ponechá na pokoji a řekne se to; nic zde nezabíjí proces, který nebyl vyžádán jménem.

Kdo volá ensure. Volá ho startovní událost providera (session.ensure_server_on_start v politice, ve výchozím stavu zapnuto): jediný okamžik, kdy je dlužen server, který ještě nikdo nespustil, je příchod agenta, a briefing, který událost zapíše, nese jeden řádek — Shared server: ready http://127.0.0.1:8741 pid 123 — aby pracovník věděl už před prvním voláním nástroje, zda nástěnka, kterou má číst, existuje. Událost nikdy nesestavuje spustitelný soubor: chybějící nebo starší než jeho zdroje se v tom řádku pojmenuje a nechá být, protože hook není místo pro spouštění kompilátoru a server ze zastaralého kódu by odpovídal stromem ze včerejška. Launcher klienta MCP (bin/majordomus-mcp) konverguje vždy stejně přes volbu; shell vstupující do repozitáře (.envrc) je informován a není obsloužen, protože project.envrc-is-an-adapter zakazuje vstupnímu hooku cokoli spouštět a shell není klient.

Oznámení přežije server, kterému bylo učiněno. Most vidí každý rámec, který jeho klient pošle, takže si drží argumenty posledního oznámení, které server přijal, a řekne je znovu, kamkoli klient dopadne dál: po opětovném připojení, jakmile je relace znovu otevřena; po převzetí na nástěnku serveru, kterým se stal vlastní proces mostu. Worker, který oznámil jednou, je na nástěnce každého serveru, který ho obsluhuje, aniž by musel oznamovat znovu; pokyn oznámit znovu po opětovném připojení zůstává v bootstrapu pro jediný případ, který most nepokryje: klient, jehož vlastní proces je serverem a restartuje se.

Před čím volba nyní chrání. Vlastník udržuje své lease mladé, zatímco se vrstva načítá (Lease::keep_alive), takže studený start pomalejší než odklad vazby se nikdy nepovažuje za opuštěný; převzetí odstraní jen soubor, který posoudilo, nikdy ten, který mezitím dorazil, a dva voliči, kteří posoudili jedno lease jako zastaralé, skončí s právě jedním lease, protože každý před odstraněním znovu přečte soubor pod zámkem vedle něj; vlastník, jehož lease bylo převzato během vazby, odmítne publikovat a degraduje, místo aby přepsal adresu vítěze, a publikuje přes vlastní dočasný soubor, synchronizovaný před přejmenováním; a server, jehož lease je později převzato, je přestane nárokovat — nic, co dělá při odchodu, se nedotkne souboru, který patří někomu jinému — po dobu odkladu zaneprázdněnosti obsluhuje peery, které má, každého z nich předá aktuálnímu serveru a skončí. Čtečka samotného serveru také zapomíná relace HTTP, které přestaly pingovat, na každé cestě, a ne jen když vlastník čeká na odchod peerů, takže mrtvý peer nikdy nezůstane attached na nástěnce.

Rozhraní pojmenovává stroj

Lokální server se váže na loopback. Tato výchozí hodnota se nehýbe: vrstva, její diagnostika, její peeři a dva nástroje, které odpovídají o .ai/local/, nejsou pro každého hostitele v síti, ve které se notebook zrovna nachází.

Jeden stroj může chtít být dosažitelný — druhý stroj se připojující přes LAN, peer meshe vytáčející se dovnitř, telefon otevírající Cockpit — a do 0.14 byl jediný způsob, jak to říct, majordomus serve --host 0.0.0.0, psané ručně. To trvalo přesně tak dlouho jako proces: serve ensure, což spouští start relace a vstup do shellu, spouští server vůbec bez hostitele, takže příští zastavení z nečinnosti nebo příští relace vrátily checkout na loopback bez jediného slova a serve status celou dobu hlásilo desired 127.0.0.1.

Rozhraní se tedy čte z jednoho místa, které dědí každý starter:

export MAJORDOMUS_HTTP_HOST=0.0.0.0     # every interface; or one address of this machine
kdo server spouštíco váže
majordomus serve--host, je-li zadáno; jinak MAJORDOMUS_HTTP_HOST; jinak 127.0.0.1
majordomus mcp (klient volící sám sebe)--http-host, je-li zadáno; jinak MAJORDOMUS_HTTP_HOST; jinak 127.0.0.1
majordomus serve ensure (start relace, vstup do shellu)nepředává žádného hostitele; server, který spustí, proměnnou zdědí
majordomus serve --deployment <id>blok listen objektu nasazení a nic jiného: deklarovaná adresa se nepřepisuje prostředím stroje
kterýkoli z prvních tří, v linked worktree127.0.0.1, když pouze MAJORDOMUS_HTTP_HOST pojmenovává rozhraní; příznak stále váže tam, kde říká. Proměnná je tvář stroje v jeho síti, což je server primárního checkoutu; server worktree obsluhuje své vlastní relace a jeho příkazy meshe jdou přes primární (MESH.md)

Z toho, že je to proměnná a ne nastavení, plynou čtyři věci:

  • Patří stroji, nikdy repozitáři. Nic sledovaného ho nemůže nastavit. Klíč v .ai/repo/policy.yaml by způsobil, že by každý klon veřejného repozitáře naslouchal v jakékoli síti, ve které se probudí.
  • Příkazová řádka vyhrává, prázdná hodnota není hodnota a hodnota, kterou nelze nikam navázat, odmítne start a pojmenuje adresu.
  • Varování platí. Vazba zvolená proměnnou stále loguje, že každý hostitel dosahující na rozhraní může číst vrstvu; řádek logu před ním říká, že adresu pojmenovala proměnná, takže vazba není nikdy nevysledovatelná. Jen objekt deploymentu deklaruje expozici a jen deklarovaná je tichá.
  • server.status odpovídá za prostředí, ve kterém se ptá. desired.host je to, co by server spuštěný z toho prostředí navázal, takže adresa, kterou server naváže, a adresa, kterou stav nazývá žádoucí, jsou jedno rozlišení (cli::resolve_http_host) a nemohou se rozcházet. Běžící server odpovídá serve status a odpovídá za prostředí, v němž byl spuštěn.

Nastavte to tam, kde to uvidí každý, kdo cokoli spouští — ve vlastním startovacím souboru shellu (~/.zshenv, ~/.profile). .envrc.local je pro jednoho z nich příliš pozdě: .envrc vyhodnotí majordomus-env enter, což zajistí server, dříve než načte .envrc.local, takže server, který spustí první cd, by proměnnou neměl, zatímco vše spuštěné z toho shellu poté by ji mělo.

Už běžící server si ponechá adresu, na kterou se navázal. Po nastavení proměnné majordomus serve stop a příští serve ensure — nebo příští relace — ho zvedne na pojmenovaném rozhraní.

Na tomto povrchu nejsou žádné účty ani tokeny, takže požadavek se posuzuje podle toho, odkud přišel (ADR 0126). Navázání mimo loopback poskytne každému dosažitelnému hostiteli povrch pro čtení a nic víc: požadavek z adresy, která není loopback, spustí schopnost jen tehdy, když schopnost čte (effect: read) nebo když je jejím vstupem podepsaná zpráva, kterou handler ověří proti politice důvěry — link.hello, link.sync a register meshe. Každá jiná trasa a každý nástroj MCP, který něco mění, odpoví volajícímu forbidden (HTTP 403) dříve, než se spustí jeho handler, včetně executions.start, který by jinak spustil libovolnou schopnost. Rozhodnutí se činí na každý požadavek, ne na relaci: id relace MCP je jméno, nikoli důkaz. Čtení se stále poskytuje celé síti, nešifrovaně; chcete-li zapisovat z jiného počítače, připojte se k loopback serveru přes SSH tunel (ssh -L 8741:127.0.0.1:8741 <machine>), jehož požadavky přicházejí z loopbacku.

Požadavek z loopbacku musí tento server pojmenovat adresou nebo localhost: požadavek, který jmenuje jiného hostitele, se odmítne forbidden dřív, než odpoví kterýkoli povrch, včetně čtení, protože přesně to posílá stránka, jejíž doména byla přinucena překládat na tento stroj (DNS rebinding). Požadavek z jiného hostitele smí stroj pojmenovat tak, jak jej zná. /swagger se servíruje s Content-Security-Policy, která připouští jen jeho připnuté soubory z CDN, jejichž integritní hashe stránka jmenuje, a jeho jediný inline skript.

Čím se spor posuzuje

O tom, který proces vlastní lease, rozhodují čtyři čísla a pátým zůstává vlastník schopen odpovídat. Jsou deklarována v bloku server: souboru .ai/repo/policy.yaml a čtou se jednou při otevření repozitáře: mcp i serve je čtou před první volbou, takže hodnoty níže jsou ty, které používá každá volba v procesu (I2129); dříve byla hodnota přečtená před politikou po celý život procesu zkompilovaným výchozím.

klíčo čem rozhoduje
probe_timeout_secondsjak dlouho sonda čeká, až současný vlastník odpoví, než se toto ticho počítá jako důkaz. Příliš málo a živý, ale pomalý vlastník je posouzen jako zastaralý a převzat zatímco ještě obsluhuje; příliš mnoho a zaseknutý drží server checkoutu tak dlouho
bind_grace_secondsjak dlouho se lease, který zatím nejmenuje žádnou URL, nechá být — server zapíše svůj lease dřív, než může obsluhovat, takže čerstvý lease bez URL je vlastník, který startuje, ne mrtvý
join_timeout_secondsjak dlouho proces čeká na připojení k souboru leasu nebo jeho vytvoření, než odmítne, místo aby čekal věčně
busy_grace_secondsjak dlouho se čeká na živého vlastníka, který neodpovídá, a ptá se ho znovu, než se jeho lease převezme. Volba dává zaneprázdněnému vlastníkovi tuto dobu a stejně tak serve ensure, než cokoli zahájí: sonda, která vyprší proti živému procesu, není mrtvý server, ale ten, který mlčí tak dlouho, je zaseknutý a nahradí se. Server, který ensure spustil po vyčkání této lhůty, je informován, který lease posoudil, takže jeho volba na něj nečeká podruhé
read_deadline_secondsjak dlouho server nechá spojení bez plně přečteného požadavku — čekání na hlavičku, udržování mezi požadavky nebo čtení těla — než spojení zavře. Klient posílající jeden bajt za sekundu by jinak držel vlákno požadavku navždy a dost takových klientů by nechalo výše uvedenou sondu bez odpovědi. Spojení, na jehož požadavek se odpovídá, nebo které bylo povýšeno na živý kanál, se tím nikdy nezavře. Každé zavření se počítá v server.status (connections.closed_by_deadline), vedle požadavků odmítnutých 503, protože všech 64 handlerů požadavků bylo zaneprázdněno (connections.refused_busy); sonda GET / se zodpoví i tehdy. Knihovna HTTP drží vlákno na každém spojení, dokud žije, a může zařadit do fronty spojení, které přijde ve vlně, dokud se nějaké neuvolní; termín je také tím, co omezuje toto čekání (I2156)
idle_secondsjak dlouho server spuštěný pro checkout, který nevlastní žádný klient — příkazem serve ensure nebo shellem vstupujícím do checkoutu — přežije svého posledního peera, než se sám zastaví; 900, pokud není deklarováno. --idle na příkazové řádce to přepíše. Server spuštěný ručně pouhým serve a bez --idle běží, dokud není zastaven, jako dříve: jeho vlastníkem je ten, kdo jej spustil (I2701)

Do 2026-09-15 byly všechny zkompilované konstanty v apps/majordomus-cli/src/lease.rs: neměnné bez přebuildování, nikde neuvedené tam, kde by to čtenář hledal, a neviditelné pro každou projekci — přitom probe_timeout je přesně to číslo, které rozhoduje, zda živý vlastník drží, co vlastní. To je rozhodnutí o tom, jak je repozitář dozorován, nikoli implementační detail.

Schéma (share/schemas/majordomus/policy/policy.v1.schema.json) vlastní blok s additionalProperties: false, takže překlepnuté časování je odmítnuto místo čtení jako chybějící; test/cases/354_a_lease_contest_is_judged_by_a_declaration.sh to drží a lease::Timings::from_policy nese doc test dokazující, že deklarovaná hodnota je ta, která se používá, a že klíč, který politika vynechá, si drží svou konstantu. Dodané hodnoty jsou konstanty, které nahradily, takže jejich deklarace nezměnila chování — záměrně: změna způsobu, jak se soutěž posuzuje, má být vidět jako změna, a ne dorazit uvnitř refaktoringu.

Spuštění z klienta

Kořen tohoto repozitáře nese konfiguraci, kterou čte každý klient, všechny jmenují stejný launcher, takže první klient, který repozitář otevře, se stane serverem a ostatní se připojí:

klientsouborco jmenuje
Claude Code.mcp.jsonserver stdio, bin/majordomus-mcp; Claude Code se jednou zeptá, zda důvěřovat serveru projektu
Gemini CLI.gemini/settings.jsonstejný launcher pod mcpServers.majordomus
Codex.codex/config.toml[mcp_servers.majordomus], načteno, když je projekt důvěryhodný
bbnic vlastníhoorchestrátor: agent, kterého spouští (Claude Code, Codex, agent ACP), čte svůj vlastní soubor výše, takže vlákno bb se připojuje přes agenta, ne přes bb (ADR 0024). Claude Code pod bb běží s settingSources: project, což načte .mcp.json; server načtený ze souboru nastavení dostane dvě sekundy před prvním tahem, takže studený checkout, který musí sestavit exekutor, ho při init ukáže jako pending a poté jako připojený
anything speaking Streamable HTTPthe running server's /mcpinitialize answers with an Mcp-Session-Id; every later request carries it; DELETE /mcp ends the session; an idle session expires and the client re-initialises on the 404, as the transport prescribes

The provider rows are the providers whose declaration names a client configuration; the whole set, with what each reads and where it keeps its scratch checkouts, is docs/generated/providers.md, generated from the same declaration.

bin/majordomus-mcp sestaví spustitelný soubor, když chybí nebo je starší než jeho zdroje (MAJORDOMUS_BUILD_PROFILE=release pro release build; MAJORDOMUS_BIN jmenuje spustitelný soubor a nikdy nebuilduje; MAJORDOMUS_NO_BUILD=1 odmítne sestavit a skončí s exit 12 s příkazem ke spuštění), nastaví MAJORDOMUS_SHARE na share/ distribuce vedle něj, nezapisuje nic na stdout a předá každý argument majordomus mcp. Klient nečeká na build. mcp je jediný příkaz, který klient spustí a pak na něj čeká, a klient, který nedostane handshake, hlásí server jako selhavší a pokračuje bez něj. Takže když tento checkout nemá spustitelný soubor ke spuštění, nebo takový, který nemůže přebuildovat — není cargo, je méně volného místa než práh buildu (MAJORDOMUS_MIN_FREE_MB, výchozí 5120), build selhal — bin/majordomus-mcp místo toho spustí nainstalovaný release (MAJORDOMUS_INSTALLED_BIN, výchozí majordomus v ~/.local/bin, kam jej dává install.sh) a na stderr řekne, který release a proč. Zastaralý spustitelný soubor, který lze přebuildovat, se přebuilduje jako dříve, MAJORDOMUS_MCP_BUILD=1 způsobí, že mcp builduje jako každý jiný příkaz, a každý jiný příkaz stále končí s exit 12, když nemá co spustit: záložní řešení je pro klienta, který by jinak byl odmítnut, nikdy způsob, jak nehlášeně spustit jiný release. Soubor uvnitř repozitáře se nikdy nebere za nainstalovaný release, ani takový, který neodpovídá na --version. ADR 0172; případ je test/cases/3177_a_client_always_gets_a_server.sh.

just mcp, just serve a just inspect v kořeni dělají totéž pro člověka; just docs-ui otevře Swagger UI běžícího serveru. Tentýž dokument, s tagy, příklady a stavy, je referencí API na webu na /docs/api/ a servíruje se surový na /openapi.json webu; instrukce initialize, OpenAPI info a GET / se otevírají jedním shrnutím z about.rs (CAPABILITIES.md).

Relace, která začíná připojením

Hook může jen přidat kontext; nic než první zpráva nezačne tah. Takže relace, která je pouze otevřena, dostane briefing a čeká, až člověk něco napíše, a dokud někdo nenapíše, není na nástěnce (ADR 0156, ADR 0172). share/shell/session-start.sh to pro Claude Code uzavírá: načtený z ~/.bashrc nebo ~/.zshrc obalí holé claude tak, že uvnitř repozitáře s vrstvou .ai/ je první zpráva majordomus prompt render session-start — vlastní prompt repozitáře (.ai/repo/prompts/session-start.md): řekni, jak jsi připojen, přečti nástěnku, najdi práci, která je dlužná, zkontroluj kolize, oznam a pak pracuj uvnitř rozsahu úlohy a vše drž lokální. Jakýkoli argument je vlastní argument člověka a předává se dál, stejně jako běh mimo dozorovaný repozitář, s MAJORDOMUS_NO_AUTOSTART=1, nebo když se prompt nedá vykreslit. Prompt je prompt repozitáře, takže jeho změna je commit a aktualizuje se každý checkout, který jej stáhne; soubor se dodává v share/ release a aktualizuje se s ním. test/cases/3221_a_bare_session_start_joins_the_cluster.sh to dokazuje v bash a zsh. Není nainstalován za vás: instalátor se nedotýká ničeho mimo svůj prefix (docs/DISTRIBUTION.md), takže načtení souboru je jediný řádek, který člověk přidá.

Claude in the browser

Three Claude surfaces meet a browser. Only one of them reaches this server today, and the other two must not be made to look as if they did:

povrchreaches this server throughreaches a page throughstate
Claude Code (terminal, desktop, IDE).mcp.json above: stdio, then the shared server, as a loopback caller that reads and changesthe Claude in Chrome extension's bridge: the mcp__claude-in-chrome__* tools, on with claude --chrome or /chromeworks: the one path that drives a page and the live mesh in one session
Claude in Chrome side panelnothing yetsamo o soběan agent of its own on claude.ai, which calls only a connector Anthropic's cloud reaches over the public internet; a server on loopback or a LAN address is out of its reach
claude.ai chat (web, desktop)nothing yet—the same: a custom connector or nothing

A tunnel from the public internet to the loopback server would give a vendor's requests the loopback caller's right to change the repository (ADR 0126 judges a caller by its address), so none is set up. The side panel and the chat reach the mesh when a vendor caller has a listener of its own that authenticates it and serves reads only (I3285, which covers a claude.ai custom connector as well as a ChatGPT one), and the hosted hub serves the mesh over TLS to an authenticated caller only (the owner's decision of 2026-10-21: TLS first, then the hub, and no anonymous caller reads the mesh). Until then a side-panel session reads what is published: https://majordomus.dev and its /ui/, a snapshot built without a running mesh, which is why it says the mesh is not observed. To work on the live mesh from a page, drive the page from Claude Code.

Set it up once, per person rather than per repository:

  • In Claude Code run /chrome and choose Enabled by default. It is a user setting: Claude Code ignores CLAUDE_CODE_ENABLE_CFC in a project's settings ("a project can't turn on Claude in Chrome", Claude Code documentation, code.claude.com/docs/en/chrome), so this repository does not try.
  • Sign the extension in to the claude.ai organization that /status names in Claude Code. Claude Code needs a claude.ai login (Pro, Max, Team, Enterprise); an API key or a setup-token keeps the bridge off.
  • The first enable writes the native messaging host — on macOS ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json, which runs claude --chrome-native-host — and Chrome finds it after a restart.

Every browser action still passes the extension's own per-site permissions; a rule in .claude/settings.json does not lift them, and this repository pre-approves no browser tool.

When it does not connect — list_connected_browsers answers an empty list, or a browser tool says "Browser extension is not connected":

  1. The extension is not running, or it is signed in to another account or organization than /status names. Sign it in again with that organization.
  2. /chrome → Reconnect extension, then restart Chrome and Claude Code.
  3. An organization IP allowlist that blocks bridge.claudeusercontent.com blocks the bridge (Claude Code documentation, code.claude.com/docs/en/network-config).
  4. A JavaScript dialog open in a controlled tab stops every later browser event until a person dismisses it.

The mesh half is diagnosed as always, from the same session: majordomus mesh doctor, and majordomus_peers for the board.

Peery

Každá relace je peer: vlastní stdio klient serveru, každý přemostěný majordomus mcp a každý klient mluvící přímo /mcp. Peer je pojmenován tím, co jeho klient poslal v initialize (clientInfo.name a version: claude-code, codex, gemini-cli, nebo jak se klient sám nazývá), číslován p1, p2, ... v pořadí připojení, s transportem, kdy se připojil a kdy byl naposledy viděn. instructions výsledku initialize řeknou klientovi URL serveru, jeho vlastní id peeru a každého dalšího peera s tím, co oznámil, před prvním voláním nástroje.

nástrojschopnostargumentyodpovědi
majordomus_peerspeers.listcheckouts?každý pracovník repozitáře s checkoutem, na kterém je, vlastní id volajícího, oznámení každého peera, každá dvojice claimů, které se potkávají, a které checkouty odpověď pokryla
majordomus_announcepeers.announceintent, scope?záznam volajícího peera a peeři, s jejichž zabraným rozsahem se střetává

Oznámení je jeden řádek záměru a cesty relativní k repozitáři, kterých se peer chystá dotknout. Nástěnka žije v paměti serveru a zmizí s procesem; peers.announce je druhu command, protože mění tuto paměť, a klientům MCP se oznamuje jako ne read-only. Přes prosté HTTP není žádný volající, takže POST /api/v1/peers/announce se odmítne (422) a GET /api/v1/peers odpovídá bez caller.

Nástěnka patří repozitáři (ADR 0044)

Server servíruje checkout, takže repozitář, na němž se pracuje přes linked worktree, má jednu nástěnku na worktree — a až do ADR 0044 odpovídal peers.list jednou z nich, zatímco bootstrap v CLAUDE.md a AGENTS.md říkal každému workeru, že viděl repozitář. Změřeno 2026-09-11: sedm živých serverů, jeden git_repository_id, sedm nástěnek, devět agentů napříč šedesáti worktree, z nichž každý četl nástěnku, která držela jen jeho.

peers.list nyní sbírá. Vyjmenuje checkouty, které git registruje, a přečte zápůjčku každého — týmiž čtečkami, jaké používá server.status — vlastní nástěnku vezme z paměti tohoto procesu a každého dalšího checkoutu, jehož server odpovídá, se zeptá na jeho vlastní nástěnku samotnou (checkouts=this). Ten parametr drží sběr hluboký jeden skok: server dotázaný na this nevyjmenovává žádný checkout a nezkouší žádný server, takže se nikdy nemůže zeptat zpět. Bez něj není žádné opakování, protože server příliš starý na to, aby parametr znal, by odpověděl celou svou nástěnkou a cyklus je jediné selhání, které to mít nesmí; takový checkout se místo toho hlásí jako nepřečtený s důvodem.

poleco říká
peers[].checkoutcheckout, na kterém peer je — jeho id, worktree, větev a zda je to tento. Není to dekorace: p1 je první relace každé nástěnky, takže sloučený výpis bez něj drží několik p1
overlapskaždá dvojice nároků, které se potkají, nyní napříč checkouty i v rámci jednoho, každý jmenující checkout, na kterém je druhý worker
boardsjedna položka na checkout, který odpověď pokrývala, dosažený či nikoli, s jeho postavením a důvodem, proč nebyl přečten
completezda šlo přečíst každou pokrytou nástěnku. false znamená, že se nešlo zeptat checkoutu — krátká nástěnka se nikdy nemá zaměnit za prázdný repozitář
checkoutsrepository (výchozí) nebo this: jen tento checkout, nic nevyčísluje, nic nesonduje, čte jednu nástěnku z paměti

Id peera je pozice na jedné nástěnce, přidělená v pořadí připojení a po opětovném připojení přidělená znovu — relace, která byla dnes ráno p3, je p1, jakmile se její most znovu připojí. Nic podle něj nesmí korelovat pracovníka v čase; checkout, který peer nese, je trvalá polovina jeho identity. Oznámení rovněž patří spojení: most zopakuje poslední oznámení svého klienta po opětovném připojení nebo převzetí a pracovník, jehož proces mostu je nahrazen, oznamuje znovu.

Stojí jedno čtení lease na registrovaný checkout, jednu sondu na checkout, jehož lease pojmenovává adresu, a jeden round trip na server, který odpoví. V tomto repozitáři 2026-09-11 — 118 registrovaných checkoutů, 7 živých serverů — trvalo vyčíslení a sondování, které server.status už provádí, 3,0 s wall.

Claim se zodpovídá, nejen zaznamenává. peers.announce porovná rozsah, který dostane, se všemi ostatními oznámeními a vrátí peery, jejichž claimy se s ním setkávají, s dvojicemi cest, které se setkávají: dva claimy se setkávají, když jsou stejné nebo je jeden uvnitř druhého (apps obsahuje apps/majordomus-cli; app ne, protože claim je cesta, ne prefix řetězce). peers.list hlásí tytéž kolize napříč celou nástěnkou, každou dvojici jednou. Stále to není vynucování, ani check --overlap shellového nástroje, které hlásí rozsahy úloh jiných worktree a končí s 0. Odmítá rozsah vlastní úlohy z start --scope, při check, při finish a v hooku pre-push — ale kolize je nyní známa v okamžiku svého vzniku, a ne objevena později v historii větve.

Nástěnka dosáhne i na workera, který o ni nikdy nepožádá. Čtení bylo dobrovolné a dobrovolná spolupráce selhala: relace se ohlásila, její transport se obnovil pod novým id peera a osm dalších ji tři hodiny nevidělo; dvě relace postavily týž subsystém, protože se ani jedna nepodívala. Proto majordomus context — příkaz, který bootstrap nařizuje každému workerovi spustit před prací — nese sekci PEERS mezi GIT a TASK: kdo další je připojen, co si každý z nich nárokuje a konkrétně které z těch nároků se dotýkají rozsahu aktuální úlohy. Čte lease sdíleného serveru (který je vázán na repozitář, takže jej linked worktree najde přes --git-common-dir v primárním checkoutu) a ptá se GET /api/v1/peers. Nápověda nikdy nenese zátěž: bez lease, bez jq či curl, se serverem, který neodpoví do dvou sekund, nebo s nástěnkou, na které nikdo není, zůstane context přesně takový, jaký byl, protože repozitář s jediným workerem nesmí dostat sekci o tom, že je sám. test/cases/106_context_peers.sh to celé drží.

Oznámení přežije spojení, které ho učinilo. Relace, která se znovu připojila, dřív mlčky ztratila vše, co řekla, sama sobě i všem ostatním; nástěnka nyní drží oznámení odešlého peera a vypisuje ho s attached: false, takže to, na čem relace řekla, že pracuje, přežije přerušený socket. Peer, který nikdy neoznámil, nezanechá nic, uchovává se nejnovějších 32 odešlých peerů, aby server, který běžel celý den, nebyl muzeum, a připojený peer se nikdy nevyhodí, aby se uvolnilo místo pro toho, kdo odešel. count z peers.list jsou skutečně připojení peeři; pole peers je delší, když nástěnka drží to, co kdo řekl, než odešel.

Epizody

Peer je spojení. Epizoda je posezení práce a pro klienta bez vlastních hooků poskytovatele je spojení jediné, co může nakreslit její hranici (ADR 0103). Do té doby bylo kreslení hranice pod modelem zapojeno jen pro Claude Code a tvrzení repozitáře, že to dělá, bylo tvrzením o jednom dodavateli.

nástrojschopnostargumentyodpovědi
majordomus_session_attachepisodes.attachexternal_idepizoda, kterou toto spojení nyní drží, zda byla obnovena, a lhůta pro opětovné připojení
majordomus_session_detachepisodes.detachexternal_id?epizoda tak, jak byla uzavřena, a co ohlásila koncová událost repozitáře
majordomus_episodesepisodes.listžádnékaždá epizoda, kterou tento server drží, otevřená i odpojená, a vlastní epizoda volajícího
stateDiagram-v2
  [*] --> Attached: initialize, a peer attaches and no episode opens
  Attached --> Open: episodes.attach opens the episode, or resumes this client's own
  Open --> Open: every message is the episode's heartbeat
  Open --> Detached: the connection goes, detached and not closed
  Detached --> Open: episodes.attach again resumes the same episode under a new peer id
  Open --> Closed: episodes.detach closes it deliberately into a session record
  Detached --> Closed: no reconnect in 15 minutes, the reaper closes it as interrupted
  Open --> Closed: the server stops, closed as shutdown
  Detached --> Closed: the server stops, closed as shutdown
  Closed --> [*]

initialize nic neotevírá. Klient, který otevře server jen proto, aby přečetl jedno pravidlo, není pracovník a nezanechá žádný záznam; attach je volání, které provede klient, který ví, že chce epizodu. To je také to, co drží záruku, kterou drží test/cases/90_mcp_shared_server.sh — obsluha repozitář vůbec nemění.

Identita patří klientovi, nikdy ne id peera. external_id je to, čemu klient trvale říká sezení, ve kterém je — id jeho konverzace nebo vlákna — a jeho celým úkolem je přežít opětovné připojení. Id peera se přiděluje na připojení a je pokaždé, když se klient vrátí, jiný řetězec; považovat jej za trvalé je to, co udělalo relaci neviditelnou pro osm dalších po tři hodiny dne 2026-09-09.

Přerušené spojení se odpojí; nezavře se. Zmizení klienta řídí dvoje hodiny a odpovídají na dvě otázky. SESSION_IDLE_TIMEOUT (90 s) rozhoduje, kdy se socket zapomene. episodes::REATTACH_GRACE (15 minut) rozhoduje, kdy je práce u konce. Čtenář logů uvidí spojení vyklizené dlouho před epizodou, kterou neslo, a je to záměr.

Nic zde nezapisuje záznam relace. Nástěnka spouští majordomus capture session --provider generic --event start|end — stejný příkaz, který spouští shim hooku poskytovatele, se stejným tvarem payloadu, přes stejný čtecí kód — a hlásí, co řekl, doslova, v poli repository epizody. Druhý zapisovač záznamu, který už hooky zapisují, by byla opakovaná sémantická definice, kterou CAPABILITIES.md zakazuje. Vlastní úložiště repozitáře je také to, co epizodu obnoví po restartu serveru: zabitý server nezapíše žádnou koncovou událost, epizoda zůstane otevřená v .ai/local/state/sessions-open/ a další attach pod stejnou identitou je session start --if-open keep, který ji zachová.

Zachycení surového promptu tu není a je deklarováno, že není. MCP server dostává initialize, volání nástrojů a oznámení; prompt osoby mezi nimi není nikdy, v žádné verzi protokolu. share/providers.yaml říká prompts: none pro generického poskytovatele, s tímto zdůvodněním v poli důkazů, a pod prompts neexistuje hodnota connection, po které by kdokoli mohl sáhnout. Co každý poskytovatel umí a kde to bylo ověřeno, je majordomus product providers a majordomus capture status.

Co rozhoduje o tom, co se obsluhuje

Program nejmenuje žádný soubor repozitáře kromě dvou konvencí, které vrstva sama dokumentuje, .ai/manifest.yaml a sources.yaml v sekci knowledge, a jak se který druh čte, zjišťuje za běhu z distribuce nástroje. Zbytek jsou data:

rozhoduječteno z
kde je vrstvanejbližší předek obsahující .ai/manifest.yaml; .git a .majordomus/ nejsou značky
které sekce existujísections: v manifestu
které soubory jsou zdroje, jakého druhu.ai/repo/knowledge/sources.yaml, jeden pathspec a druh na třídu, přes git index
jak se druh čte a jaké klíče smí néstshare/kinds.yaml a share/schemas/<kind>.schema.json v distribuci plus vlastní soubory repozitáře pod .ai/repo/knowledge/
které nástroje existujíspustitelné schopnosti s expozicí nástrojem MCP, složené pod apps/majordomus-cli/src/capability/builtin/

Důsledky, na které se repozitář může spolehnout:

  • nové pravidlo, prompt, profil, milník, úkol, tvrzení nebo dokument se servíruje, jakmile jej git add vloží do stagingového indexu, bez změny spustitelného souboru a bez restartu: staging přesune soubor, který server sleduje, takže příští požadavek přestaví vrstvu (project.the-server-sees-the-current-tree);
  • nová třída v sources.yaml pojmenovávající známý druh nebo nový druh se schématem pod .ai/repo/knowledge/ se servíruje stejně;
  • .ai/local/ se nikdy neservíruje, ať je sledován, či ne;
  • čtené YAML je podmnožina, kterou definuje SCHEMAS.md, takže soubor, který shell nástroj odmítne, se odmítne i zde, se jmenovaným stejným řádkem.

Zdroje

URIobsah
majordomus://repositoryJSON: kořen, schéma vrstvy, sekce, stav gitu, režim objevování, přítomné druhy, každá diagnostika
majordomus://scopeJSON: rozsah repozitáře tak, jak byl přečten, jeho původ a každý sledovaný soubor spočtený proti němu (SCOPE.md)
majordomus://<kind>/<identity>soubor tak, jak byl přečten, text/markdown nebo application/yaml

URI se rozřeší jednou, jednou funkcí, ať se na něj ptá kdokoli: resources/read, nástroj majordomus_get i GET /api/v1/object odpovídají na stejné URI stejně. URI, které registr nezná, se na žádném z nich nenajde. majordomus://repository je dotaz (repository.info) s expozicí zdroje: přečtený jako zdroj je to report jako text JSON; přečtený přes majordomus_get je to report jako data (answer) se stejným textem vedle (content). Registr odmítne dotaz vystavený jako zdroj, jehož vstup cokoli vyžaduje, protože čtení nic nedodá.

Objekt má také adresu, kterou URI není: majordomus://rule/project.x@1 je identita a /cockpit/objects/rule/project-x-1 je to, kam je čtenář poslán. Adresa se odvozuje z druhu a identity jednou funkcí, takže existuje pro každý objekt a je stejná na každé ploše; majordomus_entity odpovídá na obě podoby a majordomus_kinds hlásí všechny dvě identity jednoho druhu, které se redukují na jednu adresu. majordomus_entity také jmenuje veřejnou stránku dokumentace objektu (documentation: projekce, route, url, nebo reason, jeho druh není publikován), čtenou z site/data/publication.toml repozitáře a base_url webu — null, když repozitář žádnou publikaci nedeklaruje.

Identita jsou identitní pole druhu spojená pomocí @ — majordomus.scope-integrity@1 pro pravidlo, continue pro prompt, implementation pro profil, M001 pro milník — nebo, u druhu bez identitních polí (politika, dokument), cesta relativní k repozitáři. Každý vypsaný zdroj nese _meta.majordomus s druhem, identitou a proveniencí: cesta, adresář, třída, která jej objevila, sekce manifestu, pod kterou spadá, a jeho velikost.

Nástroje

nástrojschopnostargumentyodpovědi
majordomus_listobjects.listkind?, tag?objekty, shrnuté
majordomus_getobjects.geturioznačeno source: declarative, soubor vrstvy s metadaty, původem, typem média a obsahem; nebo builtin, dotaz, který URI promítá (majordomus://repository) s jeho answer, původem schopnosti a stejným textem jako content
majordomus_searchobjects.searchquery, kind?, limit?zásahy podřetězce bez ohledu na velikost písmen, každý s jedním řádkem úryvku
majordomus_entityentity.showuri?, kind? + slug?jeden objekt jako adresovatelný uzel: jeho odvozená route, reference, které deklaruje, reference, které se na něj vyřeší, plochy, které za něj odpovídají, stav spustitelných artefaktů, které jmenuje, a jeho veřejná stránka dokumentace
majordomus_kindsentity.kindsžádnékaždý druh, který index drží, s trasou jeho výpisu, kolik objektů je adresovatelných a každá kolize tras, která existuje
majordomus_repositoryrepository.infožádnédokument majordomus://repository
majordomus_scoperepository.scopežádnédokument majordomus://scope: deklarace, její původ, součet
majordomus_scope_classifyrepository.scope_classifypathzda je cesta relativní k repozitáři uvnitř rozsahu, nebo mimo něj, důvod a pravidlo, které rozhodlo
majordomus_capabilitiescapabilities.listkind?, exposure?každá schopnost s jejími projekcemi
majordomus_capabilitycapabilities.describeidjedna schopnost: schémata, původ, každá projekce
majordomus_peerspeers.listcheckouts?každý pracovník repozitáře, sesbíraný z nástěnky každého checkoutu (výše)
majordomus_announcepeers.announceintent, scope?zaznamenává, na čem pracuje volající peer (výše)
majordomus_mcpmcp.projectioneffect?popsaná projekce (níže): server, verze protokolu, transporty a připojené relace, obsluhované metody, každý nástroj s efektem a nápovědami, zapisovatelé, zdroje a stav konfigurace každého deklarovaného klienta zde
majordomus_perfperf.countersžádnéčítače práce a časování fází tohoto procesu: co se stalo jednou při startu, co se děje při každém volání
majordomus_worktreesworktree.topologyžádnédokument majordomus://worktrees: kontejner, trunk, každý worktree s jeho postavením a diagnostikou, každá větev, součty
majordomus_worktree_statusworktree.statuspath?jeden worktree — vlastní worktree repozitáře nebo ten, který obsahuje path — s jeho postavením, kanonickou cestou, nezapsanou prací a tím, zda je tam, kam patří; cesta v jiném repozitáři se odmítne
majordomus_worktree_inspectworktree.inspectbranchkanonická cesta větve, zda existuje, co cestu zabírá, worktree, který ji drží
majordomus_worktree_migration_planworktree.migration_planžádnékaždý špatně umístěný worktree s tím, kam patří, jak by se přesunul a co tomu brání; výjimky; nic nemění

Tabulka výše je základní sada, ne celek: seznam nástrojů je projekce registru schopností, roste s každým složeným modulem a seznam v próze, který by tvrdil úplnost, by byl příště nepravdivý. majordomus_capabilities a docs/generated/capabilities.md jsou odvozená, úplná reference; to, co je napsáno zde, je to, co čtenář potřebuje před jejím otevřením.

Relace a kontinuita

Dva z nástrojů odpovídají o .ai/local/ — polovině vrstvy, která jmenuje tento stroj — a jsou důvodem, proč se server váže na rozhraní loopback. Servírují se a nikdy nepublikují: žádný generátor je nezapisuje do docs/generated/ a žádná stránka webu žádný z nich nenese.

nástrojschopnostodpovědi
majordomus_continuitycontinuity.stateco by dostal pracovník pokračující zde: epizodu, na kterou ukazatel vede, aktivní úlohu, předávku a checkpoint, které se pro tento worktree a větev rozřeší, s jejich štítky, blokátory
majordomus_lifecycle_episodeslifecycle.episodeskaždá otevřená epizoda úložiště — nejen ta, kterou sleduje ukazatel — s její relací poskytovatele, jejím stavem (current, open, foreign, stranded) a posledním řádkem knihy označeným jí
majordomus_lifecycle_recoverylifecycle.recoveryepizody, které se už nemohou samy uzavřít, a příkaz, který každou vyčistí, dočasné soubory, které zabitý close nechal ve sledované sekci relací, rozložení ukazatele a aritmetika zahájených proti uzavřeným
majordomus_lifecycle_runtimelifecycle.runtimecommit, u něhož byl sestaven index tohoto procesu, proti commitu, na kterém repozitář právě je, a zda se shodují
majordomus_lifecycle_providerslifecycle.providerspro každého poskytovatele: události lifecycle, které jeho adaptér deklaruje, zda umí archivovat prompty a vynucovací záznamy, které tento repozitář zapojuje do jeho hooku
majordomus_lifecycle_closedlifecycle.closedsledované záznamy, které klon obdrží: kolik, kolik na této branch a nejnovějších dvacet

První odpovídá na otázku workera a ostatní na otázky operátora, což je jiná otázka, ne nadmnožina: continuity.state následuje state/session-current.yaml a tento ukazatel je symlink, který přesměruje nejnovější startovní událost. CONTINUITY.md obsahuje model a celou cestu.

Popsaná projekce

mcp.projection — nástroj majordomus_mcp, zdroj majordomus://mcp, trasa GET /api/v1/mcp, stránka Cockpitu /cockpit/mcp a první řádky majordomus mcp --inspect — odpovídá na to, co by tento dokument jinak musel vypisovat a ručně udržovat pravdivé:

majordomus mcp --inspect    # its lines beginning server, protocol, effect, writes and client are this answer
curl -s http://127.0.0.1:8741/api/v1/mcp | jq '{server, protocol_versions, effects, writers, clients}'
curl -s 'http://127.0.0.1:8741/api/v1/mcp?effect=repository_mutation' | jq '.tools[].name'
poleco to jeodkud pochází
server, protocol_versionskdo odpovídá na initialize a s jakými verzemikonstanty, kterými server odpovídá; verze je verzí spustitelného souboru
transportsstdio a http a relace, které jsou nyní připojeny přes každou z nichnástěnka peers tohoto procesu v okamžiku dotazu
servingmetody, které může požadavek jmenovat; že prompty a notifikace nejsou poskytoványseznam, který dispečer konzultuje, než se podívá na požadavek
tools, tool_count, effects, writerskaždý nástroj (nebo nástroje jednoho effect) s jeho schopností, účinkem a nápovědamiregistr; účinek je klasifikace schopnosti
resourceskolik schopností odpovídá na URI, kolik objektů vrstva drží, tvar URIregistr a index
clientskaždý klient, pro nějž distribuce deklaruje konfiguraci, a zda je zdejší soubor wired, foreign nebo absentshare/providers.yaml a jedno čtení každého souboru
findingskonfigurace, která existuje a nejmenuje launcher; žádný klient vůbec nenakonfigurovánodvozeno z clients, každé s nápravou

Nic v tom není vlastním seznamem, takže se nemůže rozejít s tools/list: schopnost, která získá nástroj, je v odpovědi, na stránce Cockpitu i v generované referenci při dalším požadavku, aniž by se upravil jiný soubor.

Co smí nástroj měnit

Anotace nástroje se nepíší v kódu MCP: jsou to klasifikované nápovědy schopnosti (ExecutionPolicy::hints), které vyplývají z jejího efektu, a efekt samotný nese _meta.majordomus.effect.

účinekco volání měníreadOnlyHintdestructiveHintidempotentHintopenWorldHint
readnictruefalsetruefalse
process_statepaměť tohoto procesu a nic mimo ni (peer, který se ohlašuje)falsefalsefalsefalse
repository_mutationvlastní soubory repozitářefalsetruefalsefalse

Klasifikace je konzervativní ve směru, který volající přežije: nic, co něco mění, se neohlašuje jako bezpečné k opakování, protože žádný handler nedeklaroval, že druhé volání je prázdná operace, a cokoli, co zapisuje repozitář, se ohlašuje jako schopné přepsat nebo odstranit. openWorldHint je všude false, protože žádný handler nesahá za hranice stroje: pozorování forge a konzultace poradce jsou příkazy, které spouští člověk, ne schopnosti. Které nástroje zapisují repozitář, není nikdy seznam v tomto dokumentu: jmenují je instrukce initialize, odvozené z registru, a také majordomus mcp --inspect. Každý nástroj nese kanonické id v _meta.majordomus.id a své inputSchema a outputSchema z kanonických schémat. Odmítnuté volání je výsledek s isError: true, důvodem jako textem a kategorií jako jedním slovem v _meta.majordomus.error.code — invalid_input, not_found, refused nebo forbidden, stejným slovem, jakým odpovídá trasa HTTP jako error.code, protože obojí čte z téže chyby. Odmítnutý výsledek nenese structuredContent: výstupní schéma popisuje úspěch. Neznámý nástroj, metoda nebo zdroj je chyba protokolu.

Chování při selhání

Každý řádek jmenuje selhání, co klient pozoruje a co to dokazuje: testy, které ho řídí, nebo uvedené omezení, které říká, proč to nedělá nic. Řádek bez obojího, nebo takový, který cituje test či případ, jenž tento strom nemá, test/cases/3056_every_failure_class_names_its_proof.sh odmítne.

stateco se dějedůkaz
žádné .ai/manifest.yaml nad pracovním adresářemkód 12, uvedený počáteční adresářtest/cases/3015_an_unsupervised_repository_exits_12_from_both_tools.sh
projektová data pod .majordomus/ a žádný manifestexit 12, jmenující majordomus migrateapps/majordomus-cli/tests/repository_discovery.rs::the_pre_ai_layout_is_refused_by_name
manifest nebo sources.yaml poškozený, neznámý klíč, nepodporované schémaexit 10, pojmenovaná cesta a klíč nebo řádekapps/majordomus-cli/tests/repository_discovery.rs::a_nearer_broken_manifest_is_an_error_not_a_skip
jeden soubor, který se nemůže stát objektemvyloučeno; chybová diagnostika jmenuje svou cestu a stabilní kód; index je degraded a dál slouží; --strict místo toho skončí s 10apps/majordomus-cli/tests/metadata_contract.rs::malformed_front_matter_is_named_and_excluded, apps/majordomus-cli/tests/mcp_stdio.rs::strict_refuses_a_degraded_layer_before_serving
git nepoužitelné--discovery vcs (výchozí) skončí s 13 pojmenovávajícím --discovery filesystem; blok gitu v majordomus://repository čte unavailable s důvodemapps/majordomus-cli/tests/index_behavior.rs::without_git_vcs_discovery_is_refused_and_the_walk_still_works
nenalezen žádný sdílený adresář (druhy a schémata)exit 12, jmenuje každý vyzkoušený adresář a jako nápravu jmenuje --share / MAJORDOMUS_SHAREapps/majordomus-cli/tests/metadata_contract.rs::without_a_share_directory_nothing_starts_and_the_search_is_named
neplatné schéma druhu nebo soubor schématu, nebo repozitář předefinovávající distribuovaný druhexit 10, oba soubory pojmenoványapps/majordomus-cli/tests/external_extension.rs::a_repository_defines_a_new_kind_with_its_schema_and_it_is_served_without_a_rust_change
klient zavře svou rouruproces skončí s 0, až odejde poslední připojený peer, pokud to byl serverapps/majordomus-cli/tests/mcp_stdio.rs::a_client_that_closes_its_read_end_ends_the_session
port je obsazenmísto toho se naváže volný port; obojí se zalogujeapps/majordomus-cli/tests/mcp_shared.rs::a_taken_port_falls_back_to_a_free_one_and_serve_defers_to_the_running_server, apps/majordomus-cli/tests/serve_lifecycle.rs::ensure_takes_a_free_port_when_the_one_it_asks_for_is_taken
pronájem pojmenovává server, který za tento kořen neodpovídáje převzat; stale lease se zaloguje s URLapps/majordomus-cli/tests/mcp_shared.rs::a_stale_lease_is_taken_over
sdílený server, ke kterému je most připojen, zemřemost se ujme při své další zprávě, nebo se znovu připojí k procesu, který se ho ujal; klient se nikdy znovu neinicializujeapps/majordomus-cli/tests/mcp_shared.rs::a_bridged_peer_takes_over_when_its_server_dies, apps/majordomus-cli/tests/mcp_shared.rs::a_bridged_peer_re_attaches_when_another_process_took_the_lease_first
most nemůže převzít (jeho --strict, porušená vrstva)požadavek klienta je zodpovězen chybou JSON-RPC (-32603), která pojmenovává proč; relace stdio zůstává otevřenáapps/majordomus-cli/tests/mcp_shared.rs::a_peer_that_cannot_serve_the_layer_says_so_instead_of_taking_over
soubor lease je poškozený, prázdný, nebo je bez URL déle než odklad vazbyje převzat; corrupt lease, empty lease nebo abandoned lease se zaloguje s cestouapps/majordomus-cli/tests/mcp_shared.rs::a_corrupt_lease_is_taken_over, apps/majordomus-cli/tests/mcp_shared.rs::a_lease_of_another_schema_and_an_old_empty_file_are_taken_over, apps/majordomus-cli/tests/mcp_shared.rs::an_abandoned_lease_is_taken_over_without_waiting_when_it_is_old
lease nelze vytvořit, připojit ani nahradit (souborový systém odmítající zápisy pod .ai/local/), nebo nelze spustit sdílený serverklient běží samostatně, jako by běžel --standalone: cannot use the shared server se zapíše do logu s cestou a důvodem, potom serving this client alone; žádný port, žádný lease, žádní peeři; chyby samotné vrstvy stále končí výstupním kódem uvedeným výšeapps/majordomus-cli/tests/mcp_shared.rs::an_unwritable_lease_directory_degrades_to_a_standalone_session
server dostane SIGTERM, SIGINT nebo SIGHUPzastavuje se řádně: relace a epizody uzavřeny, listenery uzavřeny, lease uvolněn, pokud je stále jeho vlastní; serve končí s 0, majordomus mcp pak zemře na signál; druhý signál nebo osm sekund jej ukončí okamžitě; jeho mosty znovu volí při další zprávěapps/majordomus-cli/tests/mcp_shared.rs::sigterm_removes_the_lease_before_the_process_dies, apps/majordomus-cli/tests/mcp_shared.rs::sigint_and_sighup_remove_the_lease_before_the_process_dies, apps/majordomus-cli/tests/lease_holder.rs::serve_stop_ends_the_server_in_order_and_closes_its_episodes
server je ukončen pomocí SIGKILLpři ukončení nic neběží; pronájem, který zanechá, pojmenovává proces, který už neexistuje, a další klient ho okamžitě převezmeapps/majordomus-cli/tests/serve_lifecycle.rs::a_stale_lease_and_a_killed_server_are_both_recovered, apps/majordomus-cli/tests/lease_busy.rs::a_silent_server_whose_process_is_gone_is_taken_over_at_once
vlastník lease žije a je zamrzlý: nic neodpovídáuchazeč čeká deklarovanou lhůtu, pak převezme lease; log jmenuje vlastníka, kterého se vzdalapps/majordomus-cli/tests/lease_busy.rs::a_live_server_that_stays_silent_loses_its_lease_after_the_grace, apps/majordomus-cli/tests/serve_lifecycle.rs::a_live_owner_that_never_answers_is_still_taken_over, apps/majordomus-cli/tests/lease_declared.rs::a_busy_owner_is_given_up_on_after_the_declared_grace_and_the_log_names_it
lease serveru převzal jiný processerver to oznámí, nepřijme žádného nového klienta a neomezený server skončí sámapps/majordomus-cli/tests/lease_lost.rs::a_server_that_lost_its_lease_says_so_and_takes_on_nobody_new, apps/majordomus-cli/tests/cov_runtime_a_server.rs::an_unbounded_server_whose_lease_is_taken_over_ends_by_itself
--http-host, --host nebo MAJORDOMUS_HTTP_HOST jmenuje adresu, která není loopbackobsluhováno, s varováním, že každý hostitel dosahující na toto rozhraní může číst vrstvu, její diagnostiku a její peery; když to zadala proměnná, říká to řádek před tímapps/majordomus-cli/tests/mcp_shared.rs::a_non_loopback_bind_is_warned_about, test/cases/992_the_local_bind_is_the_machines_to_name.sh
MAJORDOMUS_HTTP_HOST jmenuje adresu, na kterou nelze nic navázatproces skončí nenulovým kódem s cannot bind <address> a nezanechá žádný leasetest/cases/992_the_local_bind_is_the_machines_to_name.sh
klient HTTP odejde bez DELETE /mcpjeho relace vyprší po devadesáti sekundách ticha; server, jehož vlastník již odešel, tehdy skončí, nikdy pozdějiapps/majordomus-cli/tests/shared_units.rs::the_endpoint_opens_reaps_and_closes_sessions, apps/majordomus-cli/tests/serve_in_use.rs::a_server_read_over_http_outlives_its_idle_timeout_and_ends_when_left_alone
klient HTTP posílá požadavek pomalu, nebo vůbecjeho spojení se zavře při deklarovaném čtecím deadline a započítá se; ostatní klienti dostanou odpověďapps/majordomus-cli/tests/http_slow_client.rs::slow_client_released_test
každý obslužný kód požadavku je držendalší požadavek je okamžitě odmítnut jako zaneprázdněný; sonda živosti je stále zodpovězenaapps/majordomus-cli/tests/http_slow_client.rs::past_the_handler_bound_a_request_is_refused_and_the_probe_is_answered
handler požadavku se nikdy nevrátí (zamrzlý worker)jeho worker je držen, dokud běží: termín čtení omezuje příchod požadavku, nikoli jeho zpracováníomezení: netestováno — žádný test nezmrazí handler a nic jej neomezuje; odmítnutí busy výše je to, co klient potká, když je držen každý handler
disk je plnýneměřeno: lease, žurnál meshe a generovaný výstup zapisují každý vlastní chybovou cestouomezení: netestováno — žádný test ani případ nezaplňuje souborový systém, takže není ukázáno, co zápis odmítnutý pro nedostatek místa udělá každému zapisovači
adresář žurnálu meshe odmítá zápisy (jen pro čtení)na master se vlastní událost, jejíž připojení selže, nehlásí volajícímu jako nedurabilníomezení: netestováno na masteru — PR #1179 a #1196 (I2706, případ 3052) hlásí odmítnuté připojení jako nedurabilní a testují jej od začátku do konce; dokud nepřistanou, žádný test na tomto stromě to nedělá
proces je zabit mezi vytvořením lease a jeho prvním zápisemzbylý prázdný lease se převezme, jakmile je starší než lhůta vazby, jak říká řádek pro prázdný leaseomezení: netestováno jako kill — žádný test v tom okamžiku proces neukončí; stav, který zanechá, je ten, který ručně sestavuje apps/majordomus-cli/tests/mcp_shared.rs::a_lease_of_another_schema_and_an_old_empty_file_are_taken_over
selže zápis keep-alive leasuselhání se zahodí a příští tick to zkusí znovu; nic se nelogguje ani nehlásíomezení: netestováno — Lease::keep_alive zahazuje chybu záměrně a žádný test nevyvolává selhávající touch

Dva soubory jednoho druhu, které si nárokují jednu identitu, jsou oba vyloučeny a oba pojmenovány, jak vyžaduje smlouva pravidel. Nic se neopravuje, nedoplňuje výchozími hodnotami ani nepřepisuje.

Záměrně neposkytováno

  • Prompty MCP. Prompt assety repozitáře vykreslují {{CONTEXT}} z lokálního stavu checkoutu, který vlastní nástroj shellu; servírované nevykreslené by vypadaly jako hotové. Jsou to zdroje (majordomus://prompt/<name>), nikoli prompty.
  • Hierarchie bootstrap souborů. Kořenový README.md, AGENTS.md a další soubory providerů se servírují jako dokumenty se zaznamenaným adresářem; nic je nesloučí ani neseřadí, protože repozitář nedefinuje žádnou sémantiku slučování. Zaznamenáno v .ai/repo/adrs/0001-rust-cli-and-stdio-mcp.md.
  • Mutace repozitáře, která není schopností. Nástroj zapisuje do repozitáře jen tehdy, když jeho schopnost deklaruje .writes_repository(); handler je ten, který dosahuje trasa HTTP a příkazový řádek, takže odmítnutí tam je stejné odmítnutí zde a MCP nepřidává žádného vlastního zapisovatele.
  • Odběry, oznámení o změně seznamu a stream iniciovaný serverem na /mcp (tento server nic neposílá bez vyzvání). HTTP projekci téhož registru poskytuje sdílený server a majordomus serve; viz CAPABILITIES.md.
  • Trvalá koordinace. Nástěnka peerů je paměť jednoho procesu a sesbíraná nástěnka je čtení několika z nich v okamžiku dotazu: na čem peer pracuje napříč relacemi a stroji, to je záznam úlohy a rozsah nástroje shellu, ne tohle. Větev pushnutá před začátkem relace nenese žádné oznámení a jejím claimem není o nic méně; čtečkou pro ni je scripts/collision-check.

Co to dokazuje

test/cases/72_rust_mcp.sh sestaví exekutor a mluví s ním přes roury uvnitř repozitáře, který napsal init nástroje shellu, a čte majordomus://repository přes nástroj a čtení zdroje, aby viděl jednu odpověď; 76_capabilities_projections.sh přečte jednu schopnost zpět přes každé rozhraní; 90_mcp_shared_server.sh spustí dva klienty přes bin/majordomus-mcp v takovém repozitáři a ověří, že oba obsluhuje jeden server, že každý vidí druhého, že lease přichází a odchází se serverem a že tři konfigurace klientů jmenují launcher. Vlastní sada crate (cargo test --manifest-path apps/majordomus-cli/Cargo.toml) pokrývá příkazovou řádku, výběr kořene, kontrakt metadat, determinismus, kolečko protokolem, stdout jen protokolu, nemutaci, sekvenci přidat–odebrat–rozbít u externího rozšíření a v tests/mcp_shared.rs sdílený server nad skutečnými rourami a sockety: volbu, most, relace /mcp, záložní port, odkládání serve, --standalone, převzetí po zabití, opětovné připojení, zamítnutí, když přebírající nemůže obsluhovat, převzetí poškozeného, cizího, prázdného nebo opuštěného lease, dva klienty startující ve stejný okamžik, nezapisovatelný adresář lease degradující na samostatnou relaci, SIGTERM zastavující server v pořadí a nezanechávající žádný lease (tests/lease_holder.rs: signál po převzetí šetřící lease nástupce a serve stop uzavírající epizody serveru a končící 0), poškozený provoz na /mcp a průhlednost mostu: přemostěná relace a restartovaný server odpovídají bajt po bajtu tak, jak odpověděl první server. Doktrína za tabulkou selhání je pravidlo project.shared-server-resilience. tests/server_status.rs drží případ dvou worktrees a případ zastaralého lease; tests/health_server.rs drží kontrolu server z health.report proti skutečnému serveru, checkoutu, který nikdo neobsluhuje, a lease jmenujícímu adresu, na které nikdo neodpovídá, a ověřuje, že kontrola a status říkají o jednom lease jedno slovo. test/cases/992_the_local_bind_is_the_machines_to_name.sh drží rozhraní: loopback, když žádné nic nejmenuje, proměnná následovaná serve, serverem, který spouští serve ensure, a tím, který volí mcp, vítězný příznak, ignorovaná prázdná hodnota, zachované varování, shodující se desired.host a nenavazatelná adresa zamítnutá bez lease; 85_deployment_bind.sh drží vedle toho deklarovanou adresu. tests/hot_path.rs pošle stovky rámců a vyžaduje nezměněné startovní čítače (majordomus_perf); majordomus bench změří čas každého nástroje přes skutečný podřízený proces (CAPABILITIES.md). Tvrzení jsou v CLAIMS.yaml pod mcp-, hot-path-no-rebuild a benchmark-coverage-derived.

Tvrzení, která tento dokument definuje

Každý odkazuje na vlastní stránku s implementací, testem a místy použití.

Prohledává názvy a popisy všech stránek, které tento web publikuje.