86. Rustdoc crate je publikovaný webový povrch, vytvořený voláním, které jeho kontrola dokazuje, a ověřený na veřejném URL
Kontext
Poslání vlastníka z 2026-09-24 žádalo ExDoc jako plnohodnotnou plochu: generovaná reference API pojatá jako součást uzavření dokumentace, vytvářená, vkládaná, směrovaná, odkazovaná, testovaná, nasazovaná a ověřovaná tam, kde ji čte veřejnost. ExDoc patří Elixiru. Tento repozitář Elixir nemá: je to jeden crate v Rustu, apps/majordomus-cli, skripty v shellu, jq a awk a web v Zole. Ekvivalentem je rustdoc — vlastní dokumentace //! a /// crate vykreslená toolchainem, jedna stránka na exportovanou položku, se zdrojem, ze kterého byla stránka postavena.
Kvalita toho, co by se publikovalo, už vlastníka měla. #![warn(missing_docs)] v lib.rs s -D warnings v kontrole činí nezdokumentovaný export selháním sestavení a project.rust-public-api-quality (ADR 0028) měří příklady a řídkou dokumentaci ze syntaktického stromu crate, ráčnovanou .ai/repo/rust-quality-baseline.txt. Co chybělo, nebyla dokumentace. Byla to každá fáze po rendereru. Audit z 2026-09-24:
- Výstup byl zahozen.
cargo docběžel jen jako kontrola — krokcargo doc -D warningsvscripts/rust-check,RUSTDOCFLAGS='-D warnings' cargo doc --no-deps --quiet— a nikdo nečetl, co zapsal. Kontrola při každém běhu dokazovala, že se reference vykreslí bez varování, a ani jeden čtenář ji nemohl otevřít. - Žádný povrch to nedeklaroval.
web::discoverznalo trasy spustitelného souboru, web a generované zprávy podtarget/web/; strom rustdoc nebyl žádným z nich, takže ho router nemohl servírovat, domovská stránka na něj nemohla odkazovat adocs/generated/web.jsonho nejmenovalo. - Žádný krok webu to nenesl.
scripts/site-buildsestavil Zolu dosite/publica nic jiného tam nešlo. - Publikační úloha neměla Rust.
.github/workflows/pages.ymlinstaluje Node a Zola; strom by nedokázala vytvořit, ani kdyby byla požádána. - Ověření to nevidělo.
scripts/pages verifyčte/build.jsona nic jiného, takže nasazení, které ztratilo celý podstrom, by prošlo ověřením. - Žádná identita nespojovala položku se stránkou. Nic nemohlo říct „tato exportovaná položka nemá stránku“, protože nic nemapovalo položku na route, kterou jí rustdoc dává.
- Naživo každá kandidátní trasa odpověděla 404 na
https://majordomus.dev.
To je selhání, které ADR 0013 popisuje pro plochu známou na šesti místech, o krok dál: tady ji neznalo žádné místo a jediná kontrola, která se jí dotkla, dokázala vlastnost artefaktu, který pak zahodila. Úspěšné generování se implicitně četlo jako existence reference.
Rozhodnutí
Rustdoc crate je webový povrch s id rustdoc, připojený na /rustdoc, dostupný v obou světech — servírovaný majordomus serve a publikovaný na https://majordomus.dev/rustdoc/ — a každá fáze mezi zdrojem a veřejnou URL je rozhodována spustitelnou kontrolou. project.the-crate-reference-is-published je pravidlo; docs/RUSTDOC.md je provozní dokument.
| fáze | co platí | rozhodl |
|---|---|---|
| zdroj | každá exportovaná položka je zdokumentována; příklady podle quality ratchetu | cargo doc -D warnings, kontrola rust-quality |
| generování | jeden producent, vlastní vyvolání kontroly | scripts/rust-check --doc; případ 77 drží jediné volání |
| vkládání | povrch deklarovaný zjišťováním, nezávislý na checkoutu | majordomus web validate, generate --check |
| routing | /rustdoc/ servírováno ze stromu, publikováno na stejném mountu | web::files, router, případ 488 |
| references | každá exportovaná položka má svou stránku, žádná sirotčí, interní odkazy se rozlišují | majordomus quality rustdoc, scripts/ci/link-check |
| nasazení | strom složený z topologie, nikdy necommitovaný, sestavený z commitu buildu | scripts/site-build, scripts/site-check sekce 13m, scripts/site-deploy, případy 486 a 489 |
| ověření nasazení | veřejná reference jmenuje nasazený commit a je úplná | scripts/pages verify-rustdoc, kontrola pages-live, případ 487 |
Vlastní mount na nejvyšší úrovni
/rustdoc, ne cesta pod existujícím jmenným prostorem. /docs patří obsluhovanému povrchu docs a web::validate odmítne vnořený mount, který vnější povrch nedeklaroval; /api by seděl nad /api/v1, routes schopností; „reference“ zde už jmenuje několik věcí (scripts/ci/reference-check, /docs/api/, /docs/cli/ a /registry/). Povrch je pojmenován po svém producentovi, stejně jako swagger.
Deklarováno objevením z cratu
Povrch se deklaruje objevením z crate samotného, stejně jako se docs deklaruje z site/config.toml: je v topologii, kdykoli je crate, ať už producent v tomto checkoutu běžel, nebo ne. Topologie, domovská stránka a commitnutá docs/generated/web.json tedy nikdy nezávisejí na tom, že proběhl build. Průchod přes target/web/ přeskakuje adresář rustdoc stejně jako docs, built_from se čte z surface.json producenta a commitnutá projekce ho vynechává přesně tak, jako vynechává build dokumentace, protože hodnota lišící se podle checkoutu by způsobila, že každý klon hlásí drift.
Producentem je vlastní vyvolání kontroly
scripts/rust-check --doc spouští krok cargo doc -D warnings — tentýž řádek, který běží v režimech full a CI, ne druhou variantu — a předává jeho výstup: stránky se kopírují z adresáře, který cargo hlásí jako svůj target, do target/web/rustdoc/, vedle surface.json (web-surface/v1: id, mount, název, producent, dostupnost both, kategorie documentation, viditelnost public, built_from HEAD checkoutu) a přistávací index.html, protože rustdoc zapisuje index pro každý crate a žádný v kořeni. Stránka knihovního cratu je /rustdoc/majordomus_cli/index.html.
Dokumentuje se veřejné položky knihovny a spustitelný soubor, se zachovanými vlastními zdrojovými stránkami rustdocu. Reference sestavená jiným voláním, než jaké kontroluje kontrola, je druhý artefakt, který nikdo nedoložil, takže existuje jeden řádek cargo doc a případ 77 selže na druhém. --document-private-items se nepředává: ADR 0028 rozhoduje, co je veřejný povrch knihovny, a publikování soukromého by tvrdilo opak. Spustitelný soubor je dokumentován tak, jak cargo dokumentuje jakýkoli binární cíl — s vlastními položkami, protože binárka nic neexportuje — což je dnes jeho main a nic jiného. Zdrojové stránky jsou zdroj přesně v commitu, ze kterého byl strom sestaven, což je nejsilnější zdrojový odkaz, jaký reference může nést.
Necommitnuto; generováno v CI a dokázáno jako čerstvé
Strom producenta měřil 2026-09-24 u f7e05f4c5 81 MB ve 3083 souborech — stránky, které rustdoc zapsal, úvodní stránka a surface.json, sečteno podle velikosti v bajtech — a stránky vkládají hodnoty konstant COMMIT a GENERATION crate: rustdoc vykresluje pub const COMMIT: &str = "<forty hex digits>"; na stránce konstanty. Commitnutý by strom byl zastaralý při každém commitu, který se crate dotkl, a skrze tu jednu stránku při každém commitu vůbec, a vložil by celou svou velikost do každého klonu. Místo toho se generuje v CI: vlastní úlohou rustdoc v pages.yml, ne kroky úlohy nasazení, která strom předává úloze nasazení jako artefakt téhož běhu; a úlohy validačního workflow, které web sestavují, ho získávají stejnou složenou akcí, .github/actions/rustdoc, takže oba workflow ho nemohou sestavit odlišně. Úloha nasazení potřebuje úlohu rustdoc, takže běží jedna po druhé: sestavení webu skládá strom a kontroly webu posuzují složený výstup, takže ani jedno nemůže začít dřív, než strom existuje.
Čerstvost má dva svědky, kteří nesdílejí zapisovatele. built_from v surface.json je výrok výrobce o commitu; vlastní stránka crate pro jeho konstantu COMMIT je to, co kompilátor vložil při sestavení stránek. Oba musí jmenovat kontrolovaný commit a veřejné ověření čte oba.
Druhý svědek má předpoklad. COMMIT je to, co build.rs přečetl při posledním běhu, a build.rs deklaruje jeho opětovná spuštění: zdroje crate, jeho manifest, jeho lock soubor a MAJORDOMUS_BUILD_COMMIT — ne HEAD. Po commitu, který se nedotkne ničeho z toho, je prosté cargo doc čerstvé, rustdoc neběží a stránka s konstantou stále jmenuje dřívější commit, zatímco producent, který čte HEAD, zaznamená nový. To bylo změřeno 2026-09-24 s rustdoc 1.98.1 na testovacím crate nesoucím stejné deklarace opětovného spuštění. Producent proto předá sestavení commit, který zaznamenává — MAJORDOMUS_BUILD_COMMIT, což build.rs už respektuje — a oba svědci se zapisují z jedné hodnoty jedním sestavením. Dva svědci, kteří se neshodují, pak znamenají jedno: strom byl složen ze dvou sestavení.
Kompozice se odvozuje z topologie
scripts/site-build kopíruje každý publikovaný statický povrch jiný než samotný web do svého výstupu na připojení povrchu, přičemž seznam čte z docs/generated/web.json. Nenachází se v něm žádný řádek jmenující rustdoc: povrch přidaný zítra se složí zítra. Publikovaný povrch, jehož artefakt chybí, odmítne build s exit 12 a jmenuje producenta, který jej zapisuje, takže web nelze publikovat bez povrchu, který topologie říká, že nese. Připojení, které web sám už plní, se odmítne s exit 10, místo aby bylo přepsáno: jedna cesta má jednoho vlastníka. Build zaznamenává každý složený povrch s commitem, který deklaroval jeho producent, pod surfaces v /build.json a scripts/site-check (sekce 13m) odmítne složený povrch, který chybí, nenese deklaraci, deklaruje jiné id nebo připojení než topologie nebo byl sestaven z jiného commitu než build.
Výběr je jeden pomocník, mj_web_composed v lib/common.sh, nad commitnutým web.json: každý povrch druhu statický adresář, který publikace nese, kromě webu. Build, který skládá, i každá kontrola, která musí odlišit stránky webu od složeného povrchu, se ho ptají, takže žádný skript nejmenuje mount.
Stránky každého povrchu posuzuje kontrola tohoto povrchu
Smlouva stránek webu — orientační body, metadata, žádný inline styl, návrhové tokeny — posuzuje stránky, které Zola vykresluje. Stránky pod připojením jiného povrchu, odvozené z web.json, jsou z této smlouvy vyjmuty, protože markup rustdoc patří toolchainu a žádná zdejší šablona ho nezmění; posuzuje je místo toho majordomus quality rustdoc. Odkaz ze stránky webu do /rustdoc/ rozhoduje scripts/ci/link-check proti složenému stromu jako každý jiný vnitřní soubor.
Kontrola integrity se připojuje ke kanonickému inventáři
majordomus quality rustdoc [--tree DIR], schopnost quality.rustdoc, je Rust a čte vykreslený strom proti quality::source::Inventory — měření exportovaných položek crate, které už používá project.rust-public-api-quality. Vyžaduje, aby každá exportovaná položka měla svou stránku na trase odvozené z její cesty a druhu, aby neexistovala stránka položky bez položky, aby se strom přihlásil sám a jeho built_from byl HEAD, aby index crate nesl svou identitu (<title>majordomus_cli - Rust</title>), aby aktiva indexu byla přítomná, aby interní odkazy fungovaly a aby žádná stránka neuváděla cestu stroje ani tajemství. Končí 0 čistý, 10 s nálezy a 12, když není strom k posouzení — nikdy prázdný průchod. Inventář není seznam vedený pro tohoto konzumenta: je to crate, které měří samo sebe, a trasy se z něj odvozují konvencí samotného toolchainu. Stránky programu se posuzují týmž průchodem nad inventářem vlastního cílového binárního targetu.
Veřejné ověření je vynuceno
scripts/pages verify-rustdoc --commit SHA [--url URL] [--timeout N] je samostatná funkce vedle verify, ne její rozšíření. Běží jako tvrdý krok pages.yml, jakmile web obsluhuje nasazený commit, a jako sekce scripts/ci/pages-check, kontroly pages-live. Nasazení, jehož veřejná reference chybí, je rozbitá nebo sestavená z jiného commitu, selže.
To je záměrná výjimka z principu, že nic změřeného po pushi do gh-pages se nestane verdiktem běhu (docs/GITHUB_PAGES_PERFORMANCE.md), a je to v souladu s důvodem tohoto principu. Princip existuje proto, že překročení latence u nasazení, které proběhlo, není neúspěšné nasazení. Odkaz, který je na své veřejné URL rozbitý, jím je — stejná kategorie jako chyba vlastního buildu gh-pages na GitHubu, který ten workflow už červeně označuje.
Co se nemění
Politika pokrytí. Nevymýšlí se žádný práh: missing_docs s -D warnings už vyžaduje, aby byla zdokumentována každá exportovaná položka, a ratchet rust-quality hlídá příklady a tenkou dokumentaci. quality rustdoc hlásí počty, které spojuje, a nežádá nic, co stávající politika nežádá.
Kontrakt. Publikace rustdoc nedělá z Rust API crate příslib kompatibility. Crate je publish = false a ADR 0051 měří veřejný kontrakt z registru schopností. Úvodní stránka to říká slovy.
Zamítnuté alternativy
Commitnout strom. Jeho velikost a konstanty, které vkládá, z něj dělají soubor, který zastará v okamžiku, kdy se crate pohne, a zátěž každého klonu, který ho nikdy nečte. Commitnutý strom je také jeden další odvozený artefakt, u kterého je třeba dokazovat aktuálnost, když ji nasazený commit a stránka konstant dokazují příměji.
Připojit ho na /docs/rust. /docs patří servírované dokumentaci a vnořené připojení, které vnější povrch nedeklaroval, odmítá web::validate i konstrukce routeru. Deklarace vnoření by sestavení webu dala díru, o které jeho vlastní zdroj neví, a umístila by referenci do jediného jmenného prostoru, o který už tento repozitář jednou bojoval.
majordomus web compose na cestě nasazení. Skládá se z vyřešené topologie a je správným nástrojem tam, kde je spustitelný soubor po ruce, ale publikační job nenese toolchain Rustu a neměl by stavět spustitelný soubor jen kvůli kopírování adresářů. site-build čte commitnutý web.json, což je totéž rozlišení, které už promítá a na drift kontroluje generate --check, takže se nic nerozhoduje dvakrát.
Výstup JSON z rustdoc jako inventář. --output-format json je nestabilní a vyžaduje nightly toolchain; tento repozitář připíná stabilní v rust-toolchain.toml. Byl by to také druhý běh rustdoc a kontrola by pak posuzovala jiný běh, než jaký publikuje. Vlastní inventář syntaktického stromu crate je stabilní a už je mírou veřejného povrchu.
--document-private-items. Publikuje to, co ADR 0028 rozhodlo, že povrchem není, a způsobilo by to, že sada vykreslených položek by neodpovídala inventáři, se kterým ji kontrola spojuje.
cargo doc uvnitř úlohy deploy. Vkládá instalaci toolchainu Rustu, build cache crate a sestavení dokumentace do úlohy, jejíž fáze .ai/repo/ci/pages.yaml rozpočtuje, v sérii s každou z nich. Samostatná úloha není rychlejší — úloha deploy ji potřebuje, takže obě stále běží v sérii — ale drží toolchain Rustu a jeho build cache mimo úlohu deploy, klíčuje tuto cache vlastní úlohou, takže obnovuje cache sestavení dokumentace, ne validačního workflow, dělá z ceny reference samostatný řádek v měřené publikaci a spouští stejnou akci, jakou spouští validační workflow.
Samostatný skript verifikátoru. Nová shellová jednotka pro jednu funkci, která patří vedle verify, čte stejný model a sdílí jeho polling. verify-rustdoc je podpříkaz scripts/pages a kontrola jej čte přes pages-check, nikoli přes druhý vstupní bod.
Důsledky
- gh-pages roste jednou o strom, pak o jeho rozdíly. První nasazení přidá do větve celý strom — kolem 81 MB při výše uvedeném měření, než git zkomprimuje; další přidávají, co se změnilo — stránky změněných položek, konstantní stránky, vyhledávací index, úvodní stránku. Nezměněné stránky jsou tytéž bloby, takže je git uloží jednou.
scripts/site-deploystahuje jedinou větev, kterou pushuje, a tento fetch roste s ní. - Publikace čeká na referenci. Úloha nasazení potřebuje úlohu
rustdoc, takže čas od pushe k zveřejnění roste o celou dobu trvání této úlohy, stažení jejího stromu a frontu GitHubu mezi oběma úlohami. Podíl reference je samostatný řádek mezi fázemi, které úloha nasazení měří — doba běhu úlohy a stažení — a fronta mezi úlohami se hlásí jako queue, protože je GitHubova. Měří ji běh, který ji platí, ne odhad zde;.ai/repo/ci/pages.yamlrozpočtuje řízené fáze a řádek, který nejmenuje, se posoudit nedá. - Posun toolchainu může přesunout trasy. Rozložení rustdocu, názvy jeho statických souborů a konvence tras patří toolchainu. Zvýšení pinu v
rust-toolchain.tomlje může změnit aquality rustdocselže dřív, než veřejnost uvidí rozbitou stránku; odvození tras se opraví ve stejné změně jako posun. - Předmět
link-checkse mění. Nyní rozhoduje o odkazech ze stránek webu do/rustdoc/proti složenému stromu a neprochází samotné stránky rustdoc, jejichž vnitřní odkazy rozhodujequality rustdoc. Jeho jmenovatelem jsou stránky webu a říká to. - Build webu potřebuje artefakt každého publikovaného povrchu. Lokálně
scripts/site-buildodmítne běžet, dokud neproběhnescripts/rust-check --doc(nebojust rustdoc), a ten příkaz jmenuje. V CI každá úloha, která web buildí, nejprve získá strom. - Zdrojové soubory crate mohou změnit publikované bajty. Změna crate mění referenci, takže cesty, které spouštějí publikaci, musí zahrnovat to, co ji vytváří: zdrojové soubory crate, manifest, lock soubor a build skript, pin toolchainu, producenta a jeho akci. Odvozuje je, stejně jako každou spouštěcí cestu,
scripts/pages pathsz modelu kontrol a modelu publikace a nikdy se nepíší do workflow; případ 97 drží spouštěč workflow u tohoto odvození. Spouštěč, který je vynechá, nechá veřejnou referenci na posledním nasazení, zatímco master jde dál, a nic to nehlásí jako dlužné, protože kontrolapages-liverozhoduje, co je dlužné, ze stejných cest.