101. Pull requesty se integrují jeden po druhém, každý proti masteru pozorovanému chvíli předtím
Kontext
Dne 2026-09-30 bylo otevřeno sedmdesát pull requestů. Žádný nebyl připraven podle žádné poctivé definice a nic to neříkalo. Repozitář měl tři odpovědi na „co může přistát“:
- příznak
mergeableforge, který tento repozitář nemůže použít. Odvozené soubory řeší merge driver pro každý klon (merge=derived), který GitHub spustit nemůže, takže forge označí téměř každý pull request za konfliktní kvůli souborům, které generátor stejně přepíše; scripts/land, které se gitu zeptalo na správnou otázku a pak sloučilo každý čistý pull request do jedné integrační větve. To je dávka naplánovaná ze seznamu, který její vlastní první merge zastaralí, a regrese dávky nejsou nikoho konkrétního;- integrační dávky sestavené ručně (A až E), ze stejného důvodu a za stejnou cenu: červenou dávku musel bisektovat člověk.
Mezitím je mechanika pevná. Každý merge posune odvozené artefakty, které nese každá jiná větev, takže každý jiný pull request přestane obsahovat master ve chvíli, kdy jeden přistane. Pull request, který neobsahuje master, nelze sloučit, aniž by odvozená data masteru zastarala. Povinná kontrola (ci) trvá hodiny.
Rozhodnutí
Integrace pull requestů je typovaný subsystém spustitelného souboru (crate::integration) s jedním kanonickým stavem, jednou klasifikací a jedním executorem.
-
Jeden kanonický stav. Jeden
PullRequestAssessmentna otevřený pull request: co forge pozoroval, jaký je jeho head vůči masteru, jeho dispozice, důvody, důkazy, riziko, překryvy a master a head, proti nimž bylo rozhodnuto, s okamžikem, kdy forge pozoroval. Příkazová řádka, HTTP routa, nástroj MCP a Cockpit vykreslují tuto hodnotu a nic jiného. Jeho slovník je typovaný, takže žádný klient neparsuje prózu. Důvod jeReasonCode, jehož drátová podoba je řetězeccodenebocode:payload, jaký vždy byla:stacked_on:#N,base_is:BRANCH,draft,label:NAME,auto_merge_armed,merge_commit_not_allowed,superseded_by:#N,successor_open:#N,successor_not_landed:#N,successor_unread:#N,head_reachable_from_master,merge_changes_nothing,patch_ids_upstream,only_derived_artifacts_differ,relation_unknown:WHY,conflicts_on:COUNT,depends_on:#N,review:STATE,review_policy_unread,required_check_failed,behind_master:COMMITS,fork_head,required_checks:STATE,no_required_checks,required_checks_unread,dependency_cycle:#N,dependency_closed_unmerged:#N,dependency_unread:#N,declarations_unreada u připravenéhocontains_mastersrequired_checks_passedneborequired_checks_skipped. Trail, pole řetězců v OpenAPI a Cockpit čtou stejné řetězce jako dřív a kód, který nese starší trail a tento seznam ho nejmenuje, se uchová doslova. Důkaz má typovaný druh, s drátovými slovy, která měl (required_checks,review,relation_to_master,dependency,label,draft,base, plusrequired_check, jeden na každý povinný kontext,auto_mergekdykoli má forge zapnutý auto-merge,repository_settingskdyž nastavení nepovolují žádný merge commit, asupersession, jeden na každého deklarovaného nástupce, jeden na každou deklaraci, kterou učinil někdo neoprávněný, a jeden, když nebyly odkazy pull requestu přečteny celé;freshnessje rezervováno), a zdroj: pozorování forge v jeho okamžiku, nebo git na pojmenovaném masteru a headu. -
Vztah k masteru je vztah gitu.
git merge-tree --write-trees ovladači tohoto klonu agit check-attr mergepro to, které cesty jsou odvozené, z vlastních.gitattributesmasteru. Konflikt na odvozené cestě patří regeneraci, ne člověku. Výsledek je cachován dvojicí SHA, které jsou neměnné. Rozhoduje se přesně na headu, který posouzení jmenuje jako vyhodnocený: head, který v klonu není, protože se během obnovy posunul, jeunknown, nikdy vztah toho, co drží právě stažený ref. Selhané čtení atributu jeunknown, nikdy „nic není odvozeno“. Klíčem cache je jen dvojice plných id commitů aunknownse nikdy necachuje. Kde by merge konfliktoval nebo změnil jen odvozený výstup, ptá se se igit cherry: když je každý commit, který head má a master nemá, na masteru už jako shodný patch, je vztahpatch_ids_upstream, změna přistála a master ji přerostl. Částečná shoda jej nikdy nedává, ani head s vlastním merge commitem v tom rozsahu (rozlišení merge nemá patch k porovnání), ani čistý merge, který mění autorské cesty, kde master postrádá něco, co head nese, například přistálou změnu, kterou master od té doby revertoval. Vedle vztahu, ze stejné dvojice commitů a cachované pod stejným klíčem, je to, co merge mění podle druhu (ChangeShape): každá autorská cesta, kterou mění nebo na které konfliktuje, odvozené cesty a verze, kterou head deklaruje v manifestu crate, když zvyšuje tu, kterou deklaruje jeho merge base. Varianty vztahu zůstávají, jaké jsou; tvar je to, co čtou překryvy a riziko, takže konfliktní head se překrývá s ostatními přes celou svou změnu, ne jen přes cesty, na kterých konfliktuje. Překryv jmenuje svůj druh:version_bump, když oba zvyšují verzi,release, když oba mění záznam pod.ai/repo/releases/, aauthoredpro sdílené autorské cesty. Riziko jehighspaths_unknown, když je vztahunknown, nikdy „jen dokumentace“; zvýšení verze (version_bump:V), sdílené (overlapping_version_bump:#N) a sdílený release (overlapping_release:#N) jsou faktoryhigh; a.ai/repo/releases/nahrazuje odvozenéshare/version.txt, které autorské cesty nikdy neobsahovaly. -
Patnáct dispozic, rozhodovaných v jednom pořadí. Jsou to
ready(dráha připravená);needs_refresh,waiting_for_checks,waiting_for_reviewawaiting_for_dependency(čekající);needs_repairaconflicting(oprava);redundant,supersededapossibly_redundant(úklid); adraft,blocked,unsafe,other_baseaunknown(zadržené). Seznam je hodnota za běhu (PullRequestDisposition::ALL), takže povrch, který jmenuje dispozice dráhy, je odvozuje, místo aby je kopíroval. Kreadyse dospěje, jen když pull request míří na základ, není draft, nenese štítek, který jej zadržuje, nemá aktivní auto-merge, nemá deklarovaného nástupce, už není na masteru, repozitář povoluje merge commit, obsahuje aktuální master, má přistálé deklarované závislosti, splňuje politiku posouzení a na svém aktuálním headu prošly všechny požadované kontroly. To jsou kontroly, kladené v tomto pořadí:base,draft,label,auto_merge,supersession,relation_to_master,merge_method,dependency,review,no_failing_check,freshness,required_checks. Štítky, které zadržují, jsou jedna tabulka,LABEL_POLICY, každý s účinkem; politika ji kopíruje a klasifikátor čte tuto kopii a žádný jiný seznam štítků neexistuje. Dnes je každý účinekhold, porovnáváno bez ohledu na velikost písmen:do-not-merge,do not merge,blocked,hold,on-hold,wip,manual-merge. Žádný štítek nevyjímá pull request z obnovení executoru (rozhodnutí vlastníka D11). Pull request, na kterém má forge aktivní auto-merge, jeunsafe(auto_merge_armed): forge by jej sloučil sám, mimo executor a proti masteru, jaký bude, a obnovení by mu jen dalo head k merge, takže nad ním nic nepůsobí, dokud jej člověk nezruší; fronta jmenuje každý takový pull request v diagnostice. Executor slučuje jen merge commitem, protože ovladač odvozených souborů řeší merge a squash nebo rebase by přehrál commity, které nikdy neviděl. Metoda merge je protomerge, když to nastavení repozitáře dovoluje merge commit (allow_merge_commit), a jinak žádná; bez ní je každý pull request, o kterém vztah nerozhodl,blocked(merge_commit_not_allowed, s důkazyrepository_settings) a fronta říká proč. Kontrola se klade porelation_to_master, protože uzavření práce, která už je na masteru, nezávisí na nastavení merge: pull requestredundantzůstáváredundant, za svým důvodem říkámerge_commit_not_allowedaprs cleanup --applyjej stále uzavře; stejně taksuperseded. Nikdy se nevrací ke squash ani rebase. Každá kontrola se klade bez ohledu na to, co odpověděly ostatní.gatesposouzení vypisuje každou s tím, zda prošla, a jehoreasonsdrží zjištění každé selhávající kontroly ve stejném pořadí. Dispozice je ta první selhávající kontroly, takže draft, který také konfliktuje a jeho kontrola selhala, jedrafta říká všechny tři. Selhaná požadovaná kontrola se klade před aktuálností a čekající po ní: head za masterem potřebuje obnovení, které jeho kontroly spustí znovu, ledaže nějaká kontrola už selhala. Kontrola, která selže jen proto, že selhala dřívější, například aktuálnost při konfliktu merge, selže bez vlastního důvodu. Požadované kontroly jsou ochrana větve základu a pravidla (rulesets), která se na něj vztahují, dohromady; pokud nelze přečíst kterékoli z nich, nic není připraveno. Kontrola vázaná na aplikaci (app_idv ochraně,integration_idv ruleset) je jen běh kontroly té aplikace. Stav commitu stejného jména jím není a není jím ani běh kontroly jiné aplikace s tímto jménem: ten kontrolu ani nesplní, ani nezpůsobí selhání, ani ji nenechá čekat. Kontext, který dva zdroje váží na dvě aplikace, se vyžaduje od obou. Když je kontext vázán, obnovení čte aplikaci každého běhu kontroly z GraphQL rollupu forge (checkSuite.app.databaseId);gh pr listji nenese. Běh kontroly vázaného kontextu, jehož aplikace nebyla přečtena (head se mezi dvěma čteními posunul, jeho sada kontrol nejmenuje žádnou aplikaci nebo jeho kontexty se nevešly na čtené stránky), činí tuto kontroluunknown(required_checks:unknown), nikdy ne prošlou, a fronta říká, které pull requesty takovou nesou; čtení zapisovatele, které selže, způsobí selhání obnovení. Vazba jmenuje aplikaci, ne workflow: každé workflow repozitáře zapisuje jako tatáž aplikace, včetně toho, které definuje head samotného pull requestu, takže vazba drží jinou aplikaci venku a nedokazuje, které workflow běželo. (Upraveno 2026-10-07: dříve se aplikace nikdy nečetla, protožegh pr listji nehlásí, a prošel jakýkoli běh kontroly daného jména.) Z hlášení kontextu jeho stav „čeká“ určí jedno stále běžící a jinak je verdiktem nejnovější dokončené hlášení, takže selhání, které opravil nový běh, prošlo. Požadovaná kontrola, která čeká, chybí nebo je nečitelná, nebo kterou napsal kdokoli jiný než aplikace, na kterou je vázána, neprošla, a neprošla ani při přeskočení, ledaže politika přeskočení daného kontextu povoluje. Základ, který nevyžaduje žádnou kontrolu, o headu nic nedokazuje: prázdná množina po úspěšném čtení jeunknown(no_required_checks), nikdy připraveno (rozhodnutí vlastníka D5). Politika posouzení se čte z ochrany a rulesets stejným způsobem (schválení, vlastníci kódu, zastaralé zrušení) a schválení se počítá jen na commitu, na kterém bylo dáno: schválení dřívějšího headu jsoustalea dost jich na headu, kdy ještě dluží vlastník kódu, jecode_owners_pending. Rozhodnutí posouzení forge předchází politice:REVIEW_REQUIREDje čekající posouzení i tam, kde politika žádné nevyžaduje, protože to může pravidlo, které tato politika neuvádí. Závislost je deklarována jen řádkem, který začínáDepends on,Stacked on,RequiresneboLand aftera číslem; stejná slova uprostřed věty jsou próza. Splněna je jen merge: závislost uzavřená bez merge drží pull requestblocked(dependency_closed_unmerged:#N), závislost, kterou forge nedokáže vysvětlit, jeunknown(dependency_unread:#N) a deklarované závislosti, které tvoří cyklus mezi otevřenými pull requesty, blokují každého člena (dependency_cycle:#N). Head, který obsahuje head jiného otevřeného, je odvozená závislost: důkaz, nikdy blokace. Pull request je naskládán jen na větev tohoto repozitáře, nikdy na větev forku stejného jména. Nástupce se deklaruje stejně, stejným parserem: řádek těla pull requestu, který začínáSuperseded by #N, jmenuje N jeho nástupcem, a řádek těla N, který začínáSupersedes #M, jmenuje N nástupcem M, z druhé strany. Kontrolasupersessionse klade předrelation_to_master, protože pull request, jehož nástupce přistál, obvykle konfliktuje s tím, co nástupce přinesl, a je nahrazen, ne konfliktní. Deklarace se počítá jen od toho, komu to repozitář dovoluje: pull request, jehož tělo ji nese, je větev tohoto repozitáře a jeho autor jeOWNER,MEMBERneboCOLLABORATOR, v obou směrech, testováno na autorovi těla, které mluví. Cizí, forku nebo ta, jejíž příslušnost nebyla přečtena, je důkaz (possible_supersession, jmenující, kdo to řekl) a nemění žádnou dispozici. Nástupce přistál, když git najde jeho head nebo jeho merge commit v masteru, ať to forge nazývá jakkoli: sloučený squashem i uzavřený poté, co jeho head přenesla dávka, oba přistály. Oprávněný nástupce, který je stále otevřený, pull request zadržuje (successor_open:#N,waiting_for_dependency): nesmí přistát, dokud může přistát jeho nástupce, a jakmile nástupce přistane, je uzavřen, ne sloučen. Ten, který přistál, jej činísuperseded(superseded_by:#N) a posouzení jmenuje N vsuperseded_by; uzavřený bez merge jej uvolní k jeho vlastnímu vztahu; sloučený někam, co master neobsahuje, jej nechápossibly_redundant(successor_not_landed:#N) pro člověka; nečitelný jeunknown(successor_unread:#N). Pull request, jehož křížové odkazy nebyly přečteny celé, jeunknown(declarations_unread) před čímkoli z toho: nepřečtená deklarace se nikdy nepovažuje za žádnou. Z několika nástupců rozhoduje ten, který přistál, pak ten stále otevřený, pak ten sloučený jinde, pak ten nepřečtený. -
Jeden merge najednou a žádný plán ho nepřežije. Krok exekutoru nepřijímá žádný plán. Pozoruje forge, rozhodne, pozoruje znovu a jedná, jen pokud druhé rozhodnutí jmenuje stejný master a head a stále říká
ready.drainje smyčka nad tímto krokem. Neexistuje proměnná, ve které by seznam kandidátů mohl merge přežít. Merge se poté dokazuje tam, kde přistál: první-rodičovský následník rozhodnutého masteru musí být tento merge a rozhodnutý head jeho druhým rodičem; cokoli jiného jeunexpected_master, i když forge označí pull request jako sloučený. Strana této záruky na forge jerequired_status_checks.strictochrany; exekutor hlásí základ bez ní a nastavit ji neumí (D8). Neověřený merge zadrží každé další vyprázdnění, dokud ho člověk nepotvrdí (D7); přerušený merge se před dalším rozhodnutím ověří, nikdy neopakuje. -
Obnovení je samostatný, omezený akt. Když není nic připraveno,
drain --refreshpřivede master do prvního pull requestu, který ho potřebuje: merge commit s odvozeným driverem a čerstvým derive, pushnutý jako prostý fast-forward, dokud origin stále obsluhuje pozorovanou hlavu, nikdy vynucený a nikdy přepis. Přesně to už předepisujeproject.land-and-publishaprs repair <n> --applyje tentýž akt pro jeden pull request, který člověk pojmenuje, pod stejným zámkem a stopou; jeho dry run rozhoduje offline ze zaznamenaného pozorování. Pipeline je jedna hluboká: dokud obnovený pull request čeká na své kontroly, žádný další se neobnovuje, protože sloučení prvního by druhý zase posunulo za a zmařilo jeho běh CI. Drží ji jen běh, který executor spustil: povinná kontrola hlavy, kterou událostrefreshedzaznamenala jako pushnutou, dokud je pending nebo chybí. Chybějící se počítá, protože agregátní kontrola vzniká až po dokončení jobů, které potřebuje, ale drží jen po omezenou dobu po pushi (čtyři hodiny), aby kontrola, která se nikdy nepřihlásí, nezmrazila každé obnovení. Kontrola na hlavě, kterou pushnul autor, nedrží nic. -
Cleanup vyžaduje víc než merge. Uzavírají se jen dvě dispozice, a jen s
--apply. Pull requestredundantse uzavře na silný důkaz gitu: jeho head je předek masteru (head_reachable_from_master), jeho merge nemění žádný soubor (merge_changes_nothing), nebo každý commit, který má a master nemá, je na masteru jako stejný patch (patch_ids_upstream). Pull requestsupersededse uzavře, protože přistál nástupce, který pro něj deklaroval vlastník, člen nebo spolupracovník; uzavírací komentář jmenuje tohoto nástupce a netvrdí, že uzavřená práce je na masteru. Slabý důkaz se nikdy neuzavírá: ten, který se liší jen v odvozeném výstupu, nebo jehož nástupce byl sloučen někam, kam master nesahá, jepossibly_redundanta ponechá se člověku. Stáří, sdílené cesty a podobné titulky nejsou důkaz. Stopa zaznamenává uzavření jakoclosed_redundantneboclosed_superseded.Změna drátového formátu (rozhodnutí vlastníka D2, dodáno s minorem 0.13). Před ní slovo
supersededznamenalo to, co nyní znamenáredundant: silný vztah k masteru, uzavíraný úklidem s akcíclosed_superseded. Silný případ byl přejmenován naredundant, jeho uzavření naclosed_redundantasupersedednyní znamená jen výslovně deklarovaného nástupce, který přistál, sclosed_supersededjako jeho uzavřením. Klient, kterýsupersededporovnával, aby našel práci už na masteru, nyní porovnáváredundant. Každý starší řádek se čte dál: řádek stopy sclosed_supersededse parsuje a jeho důvody (head_reachable_from_master,merge_changes_nothing) říkají, který význam nesl, protože uzavření deklarovaného nástupce vždy nesesuperseded_by:#N. Vztah k masteru si ponechává své slovo,superseded(merge_changes_nothing), což je fakt gitu, ne disposice.Doplněno 2026-10-07 (rozhodnutí D1 až D5 z auditu K2/K3; navrženo, vlastník je může přepsat). D1 Deklarace je autorizována asociací pull requestu, který ji nese, na straně forge (
OWNER,MEMBER,COLLABORATOR, stejný repozitář), v obou směrech; neznámá je neautorizovaná. D2 Autorizovaná deklarace drží nahrazený pull request, dokud se nevyřeší jeho nástupce: pokud přistál, jesupersededa lze jej zavřít; pokud byl zavřen bez přistání, je uvolněn. Neautorizovaná je pouzepossible_supersessiondůkaz a nic nedrží ani nezavírá. D3 Přistání je fakt gitu — head nebo merge commit nástupce je předkem základu — takže se počítá i nástupce, který se dostal na master v dávce;MERGEDforge důkaz pouze vyslovuje. D4 Deklarace o otevřeném pull requestu se čtou z jeho vlastních křížových odkazů (stránkovaně) a z jeho vlastního těla, nikdy z omezeného hledání mezi zavřenými pull requesty; částečné čtení tento pull request drží (declarations_unread) a selhané čtení způsobí selhání pozorování. D5 Pravidlo má verzi 3 a jmenuje všechny čtyři důvody uzavření: předky, merge bez změn, každou záplatu v upstreamu (což odrážka verze 2 nikdy nejmenovala, ačkoli kód podle ní zavíral) a autorizovanou deklaraci, jejíž nástupce přistál. Před touto změnou držel a zavíral #MSupersedes #Mjakéhokoli těla, zavřené deklarace pocházely z 200 nejnovějších výsledků hledání a schéma pozorování mělo verzi 3; nyní má 4.Dodatek k D3, 2026-10-07 (rozhodnutí R1; zúžení, které může vlastník zvrátit). Fakt z gitu je důvěryhodný jen o head, který nikdo nemohl přesunout, aby ho vyrobil. Útok: otevřený #1 vlastníka říká
Superseded by #7, kde #7 je pull request z forku přispěvatele; #1 je na něm podle D2 zadržen. Autor forku force-pushne větev #7 na libovolný starý commit masteru a #7 zavře. Jeho head je pak předkem masteru a D3 v původním znění to označilo za přistálé:prs cleanup --applyzavřelo #1, ačkoli na masteru nebyla žádná práce z #7. Nástupce, o kterém forge nehlásí, že byl sloučen, se tedy počítá jako přistálý jen tehdy, když jeho head žije v tomto repozitáři a je předkem báze (cesta batch-landed); nástupce z jiného repozitáře zavřený nesloučený nikdy nepřistane, ať ukazuje head kamkoli, a uvolní zadržení stejně jako nástupce zavřený bez přistání. Nástupce, o kterém forge hlásí, že byl sloučen, přistane, když jeho head nebo jeho merge commit je předkem báze, fork nefork. Kde žije head vyřešeného nástupce, se čte spolu s ním a nepřečtené je fork. Ve stejném duchu nástupce, o kterém forge říká, že nemění žádný soubor, nepřinesl masteru nic a nikdy nepřistane, čímž se uzavírá stejný trik s větví tohoto repozitáře resetovanou na master.Rozhodnutí z 2026-10-07 o D4 a o tom, co se počítá jako deklarace. R3 Čtení deklarací, na které forge neodpoví, shodí celou obnovu (exit 12) a nezaznamená žádné pozorování: to zaznamenané před ním zůstane, jak bylo. Jen zkrácené čtení je podržení u pull requestu. R4 Řádek uvnitř citace markdownu (
>), ohraničeného bloku kódu nebo komentáře HTML není deklarací jeho autora; D1 vložilo veškerou důvěru do toho, čí tělo mluví, takže se počítá jen to, co toto tělo uvádí. Závislost si ponechává shovívavé čtení: může pull request jen nechat čekat. R5 Otevřený deklarující, jehož příznak repozitáře čtení deklarací neuvedlo, se čte jako mezirepozitářový a nic neautorizuje.Zbytky, 2026-10-07 (známé, přijaté, k opětovnému posouzení vlastníkem). R2 Zkrácení drží a cizí osoba ho může způsobit: limit stránek počítá každý křížový odkaz na pull request a zmínka z úkolu nebo z jiného repozitáře se zahodí až poté, co byla projita po stránkách. Kdo napíše více zmínek o
owner/repo#N, než se přečte (padesát stránek po sto), drží #N jakounknown(declarations_unread) při každém obnovení; žádný příkaz ho neuvolní a rozhoduje člověk. To je blokace a nikdy uzavření, stojí cizí osobu jednu napsanou zmínku na kus a zbytek fronty se hýbe dál. Žádný mechanismus uvolnění se zde nevymýšlí. R6MEMBERje slovo forge pro člena organizace bez ohledu na jeho přístup k tomuto repozitáři a vyhodnocuje se při pozorování, ne při napsání těla. Bývalý spolupracovník, který je stále členem, může upravit tělo svého starého pull requestu v tomto repozitáři naSupersedes #M: je-li ten pull request otevřený, drží #M, a byl-li dávno sloučen, #M jesupersededa uzavíratelné. D1 jmenujeMEMBERa platí; osobní repozitář nemá členy a není vystaven. -
Jeden exekutor na základní větev. Výhradní lease pod společným git adresářem: výhradní
flockna jeho souboru, držený po dobu života exekutora, s držitelem zaznamenaným vedle pro toho, kdo čte. O držiteli rozhoduje jádro, takže exekutory spuštěné současně nemohou vyhrát oba, a držitel, který skončí, i pádem, ho okamžitě uvolní; živý držitel není nikdy převzat, jakkoli starý je jeho záznam. Mez zastarání je to, co hlásí pozorovatel, ne licence k převzetí lease. Pozorovatelé ho nikdy nepřebírají.Lease chrání jeden klon: každý worktree sdílí jeho společný adresář gitu a druhý klon na jiném stroji má svůj vlastní. Repozitář chrání mesh (ADR 0067), když ho běží sdílený server tohoto checkoutu: před lease si exekutor vezme výhradní nárok mesh na
integration/<repository>/<base>jako relaci svého checkoutu, proti které se každý propojený runtime připouští. Exekutor druhého stroje je odmítnut s exit 12, jmenující držící relaci a co o svém účelu řekla, dříve než si vezme lease nebo jedná. Klíč nároku se zapíše do záznamu lease a uvolní po lease. Mesh, který zde neběží, neodmítne nic: chrání samotný lease a záznam,prs briefi Cockpit to říkají („per-clone guard only“). Nárok, který exekutor zanechal při ukončení, je vlastní relací jeho checkoutu, kterou mesh této relaci nikdy neodmítne; další exekutor tam vezme lease a zbylý nárok uvolní, takže pád stojí ostatní stroje čekání, nikdy nárok, který nikdo nemůže uvolnit. -
Síť je deklarována, ne tolerována.
prs refresh,prs drain,prs cleanupaprs repair --applyspouštějí GitHub CLI agit fetch. Každý jiný povrch vykresluje zaznamenané pozorování s jeho stářím. SECURITY.md jmenuje výjimku. Kromě otevřených pull requestů pozorování čte u každého otevřeného pull requestu asociaci jeho autora a pull requesty, které ho zmiňují (gh api graphql, stránkované; zmínka je deklarace, jen když tělo onoho pull requestu říkáSupersedes), a každého nástupce nebo závislost, kterou některý otevřený jmenuje a která není otevřená (gh pr view), a stahuje jejich hlavy, aby git mohl říct, zda nástupce přistál poté, co přestal být uváděn jako otevřený. -
Každý úkon se zaznamenává. Výběry, rozhodnutí o zastaralosti, pokusy o merge, merge s masterem před a po, odmítnutí, obnovení a uzavření jdou do stopy jen k připojování, kterou obsluhuje schopnost
integration.events. Stopa patří repozitáři, ne checkoutu: jeden soubor pod společným adresářem gitu,<git-common-dir>/majordomus/integration/events.jsonl, vedle lease, takže každý worktree zapisuje a čte tentýž. Každý úkon se připojí dříve, než se provede, a úkon, který stopa nemůže zaznamenat, se neprovede. -
Třídy selhání a opakování. Každý neúspěšný čin se zaznamená se svou třídou:
stale,conflict,new_failing_check,review_revoked,transient,policy_violation,verification_failednebounreadable. Prvních šest patří jednomu kandidátovi a odvod pokračuje dalším; poslední dvě patří repozitáři nebo exekutoru a odvod se zastaví. Kandidát, kterého forge odmítl mergovat nebo jehož obnovení selhalo, se drží stranou jakoneeds_repair(executor_merge_refused,executor_refresh_failed), dokud jsou odmítnuty jeho head a master — složeno ze stopy, nikdy z plánu — takže jedna odmítnutá změna neblokuje frontu. Přechodné selhání nezdržuje nic. Forge se ptá znovu jen při výpadku (crate::integration::retry, případ 742), merge nikdy; souvislý odvod vyčká ažCONTINUOUS_TRANSIENT_LIMITpo sobě jdoucích výpadků, každý zaznamenaný jakoobserve_failed, než skončí. -
Průběžný režim je ohraničená smyčka ohraničených odtoků.
prs drain --continuousdrží lease po celý svůj běh a opakuje jeden ohraničený odtok za cyklus, takže každé akci stále předchází čerstvé pozorování a žádný plán nepřežije merge. Mezi cykly čeká interval uvnitřINTERVAL_SECONDS(30 až 900 s: dolní mez šetří forge, horní drží čekajícího workera hluboko uvnitřLEASE_STALE_AFTER), po jednosekundových dílcích, takže signál je respektován do sekundy po dokončení probíhajícího kroku. Zastaví se na signál, na merge, který nešlo ověřit, a poCONTINUOUS_TRANSIENT_LIMITpo sobě jdoucích výpadcích. Zkušební běh nikdy není průběžný. -
Čekání je fakt a vyhladovění se ukazuje, nevykupuje. Stopa zaznamenává, kdy se pull request stane použitelným k akci a kdy jím přestane být, a při každém výběru použitelné pull requesty, které byly přeskočeny;
crate::integration::waitto sloučí na to, jak dlouho každý čekal a jak často byl zvolen jiný. Pull request přeskočenýSTARVING_AFTERkrát je označen jako vyhladovělý. Pořadí se tím nemění: jeho rozhodování při shodě už preferuje starší ze dvou jinak stejných kandidátů a dlouhé čekání nikdy není důvodem sloučit dříve něco méně bezpečného. -
Rollout je po etapách a stopa je jeho záznam. Etapa 1 je zkouška nasucho proti skutečnému repozitáři (
prs refresh,prs status,prs drain --dry-run), která nic nepřesune. Etapa 2 je jeden omezený merge (prs drain --max 1), etapa 3 malé omezené drainy (--max 3, pak--max 5). Etapa 4, kontinuální režim, je odmítnuta (exit 10, před lease, base či forge), dokud stopa nedržíROLLOUT_MERGES_BEFORE_CONTINUOUS(pět) ověřených mergí od posledního merge, který nešlo ověřit; takový merge začíná počítání znovu, ať byl potvrzen, nebo ne, protože záznam, který ukončuje, byl důkazem. Výchozí hodnoty jsou bezpečné:prs status,planaexplainčtou; drain mergeuje, jen když je o to požádán; cleanup nic neuzavře bez--apply.
Důsledky
Propustnost omezuje povinná kontrola: jeden pull request přistane na jeden běh CI jeho obnovené hlavy. To je skutečná cena mechaniky tohoto repozitáře a plánovač ji dělá viditelnou, místo aby ji schovával do dávky. Paralelní spekulativní obnovy — merge queue — jsou pozdější rozhodnutí a potřebují nejprve důkazy z tohoto.
scripts/land je smazáno. scripts/force-merge-all, které inventář automatizace už určil ke smazání, se maže s ním. scripts/ci/backlog-check --remote měří backlog přes majordomus prs.
Zamítnuté alternativy
- Důvěřovat
mergeableforge. Je tu špatně v obou směrech: změřeno CONFLICTING na čistých mergích a MERGEABLE na skutečném konfliktu. - Sloučit každý zelený pull request postupně z jednoho seznamu. Každý merge seznam zneplatní. O druhém merge by se rozhodovalo proti masteru, který už neexistuje.
- Dávky. Vyměňují přiřaditelnost za propustnost a červená dávka stojí člověka bisekci. Zůstávají rozhodnutím člověka, ne executoru. Dne 2026-10-16 vlastník toto rozhodnutí učinil: role přistání přistává kombinované tops (ADR 0167) a zde popsaný executor se nemění.
- Sloučit pomocí
--admin. Odmítnutí ochrany větve je odpověď, ne překážka. - Zavírat pull requesty podle stáří nebo podobnosti. Ani jedno není důkaz, že práce přistála.
Doplnění, 2026-10-15: merge, který provede GitHub, hlídají kontroly, ne strict (I1409)
D8 nechalo required_status_checks.strict vlastníkovi a nazvalo to polovinou ochrany na straně forge. I1409 položilo užší otázku, kterou to nechává otevřenou: když forge provede merge na master, který se posunul poté, co běžel ci pull requestu, co odmítne zastaralý odvozený strom dřív, než se publikuje? Změřeno 2026-10-15, ochrana vyžaduje ci s strict: false a enforce_admins: false. Odpovědí jsou tři kontroly, každá na stromě, který posuzuje, a žádná z nich není strict.
- Před mergem požadovaná
ci. Běh pull requestu si stáhnerefs/pull/<n>/merge, merge, který by forge v tu chvíli provedl, a jeho úlohasitetam spouštíscripts/generate-site-data --checka jeho úloharusttam spouštímajordomus generate --check(.github/workflows/validate.yml, úlohasite, krokderived site data in sync). - Mezi tímto během a sloučením je to vlastní textové slučování forge. Forge nespouští žádný vlastní merge driver. Když je
merge.derivednedefinováno, git sáhne po textovém slučování, což je přesně to, co říká.gitattributes. Každý odvozený strom zaznamenává jednosource_hashvsite/data/generated/source.jsona tento hash pokrývá deklarované vstupy webu: 1497 cest včetně projekcí registrudocs/generated/{registry,graph,openapi, cli,web,changelog}.json. Dvě strany, z nichž každá změnila kanonický vstup, změnily ten samý řádek, takže forge hlásí pull request jako konfliktní a nesloučí ho. Ukazuje to holý repozitář bez nakonfigurovaného driveru:git merge-tree --write-treemasteruc0e3585dc8s hlavami #1159 a #1149, oba na GitHubu CONFLICTING, uvádí mezi konfliktysite/data/generated/source.json. Pokud kanonické vstupy změnila jen jedna strana, sloučené vstupy jsou vstupy této strany a odvozená data jsou jejich projekcí (případ 3048 drží nesouvisející merge aktuální). - Po merge, před publikací, mergnutý commit. Push na master spouští
validate.ymlna merge commitu, se stejnými kontrolamisitearust, apages.yml, jehož jobdeployspouštíscripts/pages build. Tento příkaz končí s kódem 10 na zastaralých datech webu (cmd_current) a job ho spouští před krokempublish. Merge masteruc0e3585dc8commitoval GitHub. Na něm validate run 38635619021 (job 116098148462) prošelderived site data in synca pages run 38635619039 (job 116105040387) spustilbuildv 21:07:39Z apublishv 21:09:30Z. Zastaralá data webu jsou odmítnuta nabuild, takže gh-pages drží poslední aktuální commit a kontrolapages-livejmenuje dluženou publikaci.
Rozhodnutí. Ochranou na straně serveru je kontrola čerstvosti v čase CI, na třech výše uvedených místech. Nejsou to povinné kontroly strict a není to nová kontrola v čase merge. Dokumenty tvrdí přesně to: .githooks/pre-commit chrání merge, který commitne člověk, a výše uvedené kontroly chrání ten, který commitne forge. Žádný dokument nesmí tvrdit, že hook chrání merge na GitHubu.
Rejected:
strict: truejako stráž. Je to nastavení repozitáře a akt vlastníka (D8, mimo rozsah I1409). Znovu by spouštělocipro každý otevřený pull request pokaždé, když se master pohne, což je propustnost, kterou tento záznam už oceňuje na jedno přistání na běh. Nepokryl by ani merge--admin, protože administrátoři nejsou vynucováni.- Merge fronta. Je to „pozdější rozhodnutí“ z Důsledků výše. Potřebuje nejdřív důkazy tohoto ADR a pro odvozená data se nedluží, protože bod 2 už nutí forge odmítnout jedinou kombinaci, ve které by textový merge mohl pro web udělat chybu.
- Znovu odvozovat v buildu Pages. Non-scope I1409: generování není deterministické podle stromu, takže by build vytvořil třetí odpověď.
Zbytek, uvedený, ne zamlčený. Odvozený artefakt mimo deklarované vstupy webu, například generovaná oblast AGENTS.md nebo dovednost pod .claude/skills/, mohou změnit dva pull requesty dotýkající se různých řádků a forge může tyto změny textově čistě sloučit do výsledku, který není projekcí. Bod 1 ten merge nevidí a bod 2 ty soubory nepokrývá. Až generate --check bodu 3 na běhu push to odmítne jako první: master zčervená dřív, než se na něj kdokoli spolehne. Žádný z těch bajtů web nepublikuje.