runtime-lifecycle — Jeden použitelný server na checkout se spustí, připojí, zastaví a ohlásí svůj stav bez souběhů nebo skrytých zabití
Vstup do checkoutu, otevření Cockpitu nebo spuštění libovolného počtu klientů MCP konverguje k jednomu serveru pro daný checkout, který se přebírá, jen když je mrtvý, zastavuje se v pořadí, když je o to požádán, odstraňuje jen svůj vlastní lease, hlásí typovaný stav, který čte každý povrch, a zůstává nahoře, dokud ho některý povrch používá.
AKTIVNÍ 7 of 10 issues done
- připraveno
- 2
- blokováno
- 1
- aktivní
- 0
- verify
- 0
- hotovo
- 7
- zrušeno
- 0
Problém
Volba sdíleného serveru je zdravá v kroku vytvoření (O_CREAT|O_EXCL v apps/majordomus-cli/src/lease.rs:516-520, dokázáno apps/majordomus-cli/tests/mcp_shared.rs::a_storm_of_clients_converges_on_one_server_and_one_board a případem 191), ale to, co následuje, ne. Sonda leasu je GET / na týchž čtyřech workerech, které provádějí volání nástrojů (apps/majordomus-cli/src/lease.rs:896-925, apps/majordomus-cli/src/http/server.rs:23), takže čtyři dlouhá volání způsobí, že zdravý server vypadá mrtvě, a nový klient ho převezme. Server, který lease ztratil, si své přemostěné relace ponechává navždy, protože mosty pingují každých 20 s (apps/majordomus-cli/src/mcp/bridge.rs:19) a smyčka serve je počítá jako připojené (apps/majordomus-cli/src/commands/serve.rs:136-146). `serve stop` je SIGTERM, jehož handler bezpodmínečně odlinkuje lease a znovu vyvolá signál (apps/majordomus-cli/src/lease.rs:1164-1212), takže může smazat lease nástupce a nikdy nespustí uspořádané zastavení (apps/majordomus-cli/src/shared.rs:282-289). Časování leasu, která politika deklaruje, se nikdy nedostanou k volbám mcp a serve (apps/majordomus-cli/src/lease.rs:89-191 OnceLock vyplněný před během apps/majordomus-cli/src/policy.rs:364). Stav životního cyklu je čtení souboru pozorovatelem (apps/majordomus-cli/src/capability/builtin/server.rs:163-177) bez degraded, stopping nebo failed a živý server z 2026-10-09 osm hodin nic nelogoval, protože zdědil MAJORDOMUS_LOG=error (apps/majordomus-cli/src/commands/serve.rs:727-729). Server s UI na prvním místě se zastaví po 900 s, zatímco je prohlížen, protože za použití se počítají jen relace MCP (apps/majordomus-cli/src/shared.rs:250-253). Změřeno v základním auditu runtime z 2026-10-09 na master 1fb8d6be44 (plan/RUNTIME_BASELINE.md).
Výsledek
Vstup do checkoutu, otevření Cockpitu nebo spuštění libovolného počtu klientů MCP konverguje k jednomu serveru pro daný checkout, který se přebírá, jen když je mrtvý, zastavuje se v pořadí, když je o to požádán, odstraňuje jen svůj vlastní lease, hlásí typovaný stav, který čte každý povrch, a zůstává nahoře, dokud ho některý povrch používá.
Aktuální stav
Implementováno a otestováno: atomické vytvoření, publikovaná URL až poté, co socket obsluhuje (apps/majordomus-cli/src/shared.rs:294-305), zastaralé, poškozené, opuštěné, zaneprázdněné a nahrazené leasy, záložní port, hygiena stdio. Částečně: sonda, převzetí a publikace jsou compare-then-act (apps/majordomus-cli/src/lease.rs:778-798, 1082-1107); existující relace ztraceného serveru se obsluhují bez konce (apps/majordomus-cli/src/http/mcp.rs:113-118); SIGTERM není graceful (apps/majordomus-cli/tests/mcp_shared.rs::sigterm_removes_the_lease_before_the_process_dies tvrdí signál 15); deklarované timings jsou v mcp a serve mrtvé. Chybí: stavy degraded, stopping a failed a životnost z nepoužití MCP. Vyvrácené premisy balíku: neexistuje příkaz `majordomus ui`, `serve ensure` a `env enter` jsou vstupy UI-first (apps/majordomus-cli/src/commands/serve.rs:481-576, apps/majordomus-cli/src/commands/env.rs:701-760); `serve ensure` a `serve stop` jsou podle deklarovaného návrhu jen CLI a ne schopnosti; apps/majordomus-cli/src/served.rs je pozorování nasazení, ne životní cyklus serveru; nejvyšší `majordomus doctor` je governance doctor (lib/doctor.sh:53-77) a o serveru nekontroluje nic. Launcher, který přestavuje spustitelný soubor v primárním checkoutu, je I2059.
Cílový stav
Každá porucha životního cyklu v matici auditu má test: N souběžných prvních klientů, zaneprázdněný vlastník, zabitý vlastník, ztracený lease se živými mosty, SIGTERM v závodě s převzetím, deklarované časování, uživatel jen UI. server.status odpovídá jedním z absent, starting, ready, degraded, outdated, stale, stopping nebo failed, rozhodnutým v jedné funkci. docs/MCP.md a README.md popisují server, který se dodává: jeden na checkout, co zapisuje, kdy končí.
Rozsah
- apps/majordomus-cli/src/lease.rs
- apps/majordomus-cli/src/shared.rs
- apps/majordomus-cli/src/commands/serve.rs
- apps/majordomus-cli/src/commands/mcp.rs
- apps/majordomus-cli/src/commands/env.rs
- apps/majordomus-cli/src/http
- apps/majordomus-cli/src/capability/builtin/server.rs
- apps/majordomus-cli/src/capability/builtin/health.rs
- apps/majordomus-cli/src/policy.rs
- .ai/repo/policy.yaml
- apps/majordomus-cli/tests/
- test/cases
- docs/MCP.md
- README.md
Mimo rozsah
- Příkaz `majordomus ui`, pokud záznam I2131 nerozhodne, že je potřeba nad rámec `serve ensure`.
- Jeden server pro všechny repozitáře. ADR 0044 rozhodlo o jednom na checkout a nástěnka se sbírá napříč nimi.
- Řádné ukončení uvnitř produkčního kontejneru, což je I0906 (fly-deployment).
- Opakované odeslání volání nástroje mostem, což je I2145 (mcp-transports-are-reliable).
Kritéria přijetí
- Server, jehož čtyři pracovníci jsou zaneprázdněni voláními nástrojů, nikdy nepřevezme klient, který se mezitím spustil
- Server, který ztratil lease, nemá po deklarované době odkladu žádnou relaci a skončí sám
- Žádný proces neodstraní soubor lease, jehož token není jeho vlastní, prokázáno při SIGTERM v závodě s převzetím
- `serve stop` provede uspořádané zastavení a nenechá žádnou otevřenou epizodu
- Časování lease deklarované v policy.yaml je to, na které čekají volby mcp a serve
- server.status hlásí stopping, failed a degraded, každé z testu, který ten stav vytvoří
- Server používaný jen přes Cockpit nebo /events se nezastaví jako nečinný
Validace
- bin/majordomus plan validate
- cargo test --test mcp_shared --test serve_lifecycle --test lease_busy --test lease_lost --test lease_declared --test server_status
- MJ_TEST_JOBS=4 bash test/run.sh 90_mcp_shared_server 113_shared_server_survives_a_crash 191_entry_races 192_entry_across_checkouts_and_clients
Požadované důkazy
- lifecycle_fault_tests
- typed_server_standing
- docs_describe_the_shipped_server
Rizika
- Každý zdejší úkol upravuje apps/majordomus-cli/src/lease.rs, apps/majordomus-cli/src/shared.rs nebo apps/majordomus-cli/src/commands/serve.rs, takže nejsou navzájem bezpečné pro souběh; jeden pracovník by je měl vzít v pořadí závislostí.
- Chybné pravidlo převzetí promění jeden zdravý server ve dva. Každá změna potřebuje storm test (apps/majordomus-cli/tests/mcp_shared.rs) zelený před i po.
- Živé servery na stroji vlastníka běží s tím, co bylo naposledy sestaveno; změna schématu lease se k nim dostane jen restartem, takže během nasazení se musí tolerovat smíšená flotila.
Úkoly
Každý z nich je prováděcí kontrakt s vlastními kritérii přijetí, validačním příkazem a požadovanými důkazy. Sloupec wave je vrstva grafu závislostí, v níž úkol leží.
| issue | status | vlna | závisí na | název |
|---|---|---|---|---|
| I2126 | HOTOVO | 0 | — | Server zaneprázdněný voláními nástrojů se nikdy nepřevezme, jako by byl mrtvý |
| I2127 | HOTOVO | 0 | — | Server, který ztratil lease, předá své relace dál a skončí |
| I2128 | HOTOVO | 0 | — | Lease odstraní jen proces, který ho drží, a serve stop zastaví server v pořádku |
| I2129 | HOTOVO | 0 | — | Časování leasů, která politika deklaruje, jsou ta, která používají volby mcp a serve |
| I2131 | HOTOVO | 0 | — | Server používaný přes Cockpit nebo HTTP se nezastaví jako nečinný a checkout si udrží jednu adresu |
| I2700 | PŘIPRAVENO | 1 | I2126 | Sonda leasu se ptá levné liveness trasy a server držící jiný token leasu se nepovažuje za držitele leasu |
| I2701 | HOTOVO | 1 | I2129 | Idle timeout je deklarován vedle ostatních časování serveru a serve --idle deklaraci přepíše |
| I2702 | HOTOVO | 1 | I2131 | Checkout si udrží jednu adresu napříč restarty, protože jeho server naslouchá na portu odvozeném z checkoutu |
| I3179 | PŘIPRAVENO | 0 | — | The runtime reports one typed state, with how it got there, and every surface shows it |
| I3180 | BLOKOVÁNO | 1 | I3179 | Entering a checkout whose executable is missing or stale starts one background build and says building |
Graf
flowchart LR
I2126["I2126<br/>A server busy with tool calls is never taken over as if it were dead"]:::done
I2127["I2127<br/>A server that lost its lease hands its sessions on and ends"]:::done
I2128["I2128<br/>A lease is removed only by the process that holds it, and serve stop stops the server in order"]:::done
I2129["I2129<br/>The lease timings the policy declares are the ones the mcp and serve elections use"]:::done
I2131["I2131<br/>A server in use through the Cockpit or HTTP is not stopped as idle, and a checkout keeps one address"]:::done
I2700["I2700<br/>A lease probe asks a cheap liveness route, and a server holding another lease token is not taken for the leaseholder"]:::ready
I2701["I2701<br/>The idle timeout is declared beside the other server timings, and serve --idle overrides the declaration"]:::done
I2702["I2702<br/>A checkout keeps one address across restarts, because its server binds a port derived from the checkout"]:::done
I3179["I3179<br/>The runtime reports one typed state, with how it got there, and every surface shows it"]:::ready
I3180["I3180<br/>Entering a checkout whose executable is missing or stale starts one background build and says building"]:::blocked
I2126 --> I2700
I2129 --> I2701
I2131 --> I2702
I3179 --> I3180
classDef done stroke:#16a34a,fill:#dcfce7,color:#14532d,stroke-width:2px
classDef active stroke:#2563eb,fill:#eff6ff,color:#1e3a8a,stroke-width:2px
classDef verify stroke:#7c3aed,fill:#f5f3ff,color:#4c1d95,stroke-width:2px
classDef ready stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
classDef planned stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
classDef blocked stroke:#b45309,fill:#fffbeb,color:#78350f,stroke-width:2px
classDef cancelled stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2px
classDef superseded stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2pxOdkazováno z tohoto diagramu (10)
- I2126 Server zaneprázdněný voláním nástrojů se nikdy nepřebírá, jako by byl mrtvý
- I2127 Server, který ztratil lease, předá své relace dál a skončí
- I2128 Lease odstraní jen proces, který ho drží, a serve stop zastaví server v pořádku
- I2129 Časování lease, které politika deklaruje, jsou ta, která používají volby mcp a serve
- I2131 Server používaný přes Cockpit nebo HTTP se nezastaví jako nečinný a checkout si drží jednu adresu
- I2700 Sonda lease se ptá na levnou routu živosti a server držící jiný token lease se nepovažuje za držitele lease
- I2701 Časový limit nečinnosti je deklarován vedle ostatních časování serveru a serve --idle deklaraci přepíše
- I2702 Checkout si ponechá jednu adresu přes restarty, protože jeho server binduje port odvozený od checkoutu
- I3179 The runtime reports one typed state, with how it got there, and every surface shows it
- I3180 Entering a checkout whose executable is missing or stale starts one background build and says building
Kanonický záznam: .ai/repo/project/milestones/runtime-lifecycle.yaml. Přečtěte ho zpět pomocí majordomus plan show runtime-lifecycle.