28. Veřejnou plochu crate měří crate a absence v API je rozhodnutí
Kontext
Vše, co tento program nabízí, je projekce jednoho registru schopností. ADR 0002 to ustavilo, project.interfaces-are-projections to vyžaduje a tests/projections.rs to dokazuje v obou směrech pro MCP, HTTP a OpenAPI. Dvě věci stály mimo toto uspořádání a obě byly neviditelné ze stejného důvodu: nic je neměřilo.
Vlastní Rust povrch cratu. #![warn(missing_docs)] s -D warnings v kontrole činí nezdokumentovanou exportovanou položku selháním buildu a v den tohoto rozhodnutí byla zdokumentována každá exportovaná položka cratu. To je vše, co kompilátor umí říct. Neumí říct, zda stojí dokumentace za přečtení, a o příkladech neříká vůbec nic — kde žije zajímavé selhání, protože próza o chování tiše driftuje a spouštěný příklad ne. Při prvním měření měl crate 3595 exportovaných položek, z nichž 1191 nese chování a 149 z nich mělo spustitelný příklad. Ze 131 exportovaných modulů mělo příklad 9 a 36 nemělo vedle sebe žádný test ani žádný test, který by je jmenoval. majordomus quality report --summary počítá aktuální čísla; ta zde jsou pravdou toho dne a neudržují se.
Příkazová řádka. MCP, HTTP a OpenAPI nedeklarují nic vlastního, takže u těchto tří nemá co chybět. Příkazová řádka je jiná: nese příkazy nad rámec schopností — spouští servery, zapisuje generované soubory, vykresluje hodnoty pro člověka — a měla 53 spustitelných příkazů, z nichž 17 bylo vázáno na schopnost a 36 na nic. Při čtení jednoho po druhém byla většina v pořádku a několik ne, a neexistoval způsob, jak ty skupiny odlišit, než je číst jeden po druhém.
Třetí fakt je ten, který odpověď utvořil: tento crate je publish = false. Jeho konzumenty jsou jeho vlastní binárka, testy, benchmarky a příklady v dokumentaci. Žádný externí konzument vůbec neexistuje, takže většina z oněch 3595 nebylo API, které by někdo rozhodl nabídnout. Bylo to pub, protože pub je to, co se píše.
Rozhodnutí
Měření je schopností crate a crate je jeho předmětem. src/quality/ čte crate jako syntaktický strom — syn, procházený od src/lib.rs tak, jak jej prochází rustc, takže efektivní viditelnost se počítá a neuhaduje — a vytváří jeden typovaný QualityReport. quality.report je schopnost jako každá jiná, takže příkazová řádka, HTTP, dokument OpenAPI, MCP a Cockpit jsou projekcemi jednoho provedení. Subsystém kvality, který by potřeboval pět rendererů, by selhal v architektuře, kterou má vynucovat.
Co položka dluží, se odvozuje z položky, a co nedluží, se vypíše. Modul, typ, trait, funkce, metoda a exportované makro nesou chování a dluží spustitelný příklad. Pole, varianta, konstanta, static, alias typu a accessor, který vrací, co drží příjemce, nikoli: příklad hodnoty je příkladem čehokoli, co ji čte, a jeho vyžadování pořídí jen assert!(true) uvnitř ohrady. Tato klasifikace se počítá z druhu a tvaru položky a hlásí se jako výjimka s důvodem. Neexistuje soubor výjimek, protože soubor výjimek je místo, kam se dá přidat řádek.
Příklad se počítá, když stroj pozná, že jím je: toolchain jej zkompiluje (no_run se počítá, ignore ne), jmenuje položku, kterou dokumentuje, a tvrdí něco, co by mohlo být nepravdivé — unwrap, expect a ? jsou tvrzení, protože selhání kteréhokoli selže doctest, a assert_eq!(1, 1) ne. To záměrně není posudek o próze. Je to nejmenší sada testů, na které selžou zřejmé způsoby, jak čítač uspokojit, a tests/quality.rs má jeden fixture crate na každý způsob, takže validátor nemůže tiše zformálnět.
Příkaz je projekcí schopnosti, nebo říká, proč není. cli::LOCAL žije vedle deklarace clap a uvádí jeden z pěti důvodů: příkaz se stává procesem, zapisuje do repozitáře, odpovídá o vlastním checkoutu volajícího, je jiným jménem příkazu, který je již zaúčtován, nebo vykresluje schopnost — v tom případě jmenuje schopnost a kontrola odmítne tvrzení, pokud ta schopnost neexistuje a neodpovídá přes HTTP. Záměrně neexistuje důvod znamenající zatím nepromítnuto: čtení, které patří do API a chybí v něm, je vada, a slovník, který ji umí zdvořile vyjádřit, je slovník, který ji vyjádří.
Přísné pravidlo o povrchu je pravidlem o jeho velikosti. Položka, kterou nepotřebuje žádný konzument, stojí doc komentář a příklad a pub(crate) je levnější odpověď. Tento tlak je smyslem, ne vedlejším účinkem: proto nález o chybějícím příkladu nabízí snížení viditelnosti jako nápravu ve stejné větě jako jeho napsání.
Alternativy
Shell skript nad zdrojovými soubory. Repozitář už jeden měl pro užší otázku (scripts/ci/rust-command-check, project.rust-command-tested-in-file) a funguje, protože jeho předmětem je vyvolání makra na samostatném řádku. Tato otázka není řádková: pub fn v privátním modulu nic neexportuje, pub use vynáší typ z modulu a jeho privátní členy nechává za sebou a ohraničený blok je testem jen za podmínek, které jsou syntaktické. Pravdivou odpovědí byl jen parser, a proto je syn závislostí a grep není.
rustdoc JSON. Odpovídá přesně na tuto otázku a je pouze nightly. Repozitář se staví na stable a nepřipíná žádný nightly a gate, který potřebuje druhý toolchain, je gate, který se přeskakuje.
Vyžadovat příklad každé exportované položky, bez klasifikace. Je to pravidlo, jak by je člověk nejprve formuloval, a vyrábí 1500 příkladů konstant a polí struktur, všechny assert!(true). Klasifikace je to, co činí pravidlo vůbec vynutitelným.
Režim warn-navždy. Zamítnuto: varování, na které nikdo nemusí reagovat, je měření, ne pravidlo, a tento repozitář má měření dost.
Důsledky
Exportovaný povrch klesl z 3595 položek ve 131 modulech na 3106 v 79 snížením modulů, které žádný spotřebitel nejmenuje, na pub(crate) — padesát pět z nich, z nichž tři se vrátily, protože je jmenují jejich vlastní příklady v dokumentaci, což je kritérium v praxi, ne výjimka z něj. Toto snížení není kosmetické: činí zbývající pravidlo finančně dostupným a odstranilo devět položek mrtvého kódu, které pub skrývalo před vlastní analýzou dead_code překladače.
Každý exportovaný modul je nyní testován na chování, což v době psaní nebylo. Zbytek dluhu je politika příkladů a je zaznamenán, ne zpochybňován.
Dvě z těch devíti se ukázaly být používány testy uvnitř cratu a byly obnoveny jako #[cfg(test)] pub(crate), kam patří podpora testů. Poučení je zde zaznamenáno, protože se bude opakovat: cargo build nekompiluje kód #[cfg(test)], takže zúžení viditelnosti je třeba ověřit pomocí cargo check --all-targets a varování o mrtvém kódu z obyčejného buildu nejsou celou pravdou.
Zbývající dluh je skutečný a je zaznamenán, ne skryt: .ai/repo/rust-quality-baseline.txt drží nálezy, které stály při přistání pravidla, klíčované kódem, souborem a symbolem, ale záměrně ne řádkem, takže nález, který se v souboru posune níž, je týž nález. Kontrola selže na nález mimo něj, takže dluh může klesat a nemůže růst, a selže i zastaralá položka, takže zaplacený dluh musí ze souboru zmizet. To je mechanismus, který zavedl project.rust-command-tested-in-file a poté zrušil, když jeho dluh dosáhl nuly; od tohoto pravidla se očekává totéž.
Migrace
Pravidlo přistálo proti existujícímu stromu, takže přistálo s rohatkou. Nový kód je mu podřízen plně od prvního commitu — kontrola zachytila novou stránku Cockpitu této změny ještě před commitem, což je ta důležitá ukázka. Stávající dluh se odpracovává soubor po souboru a pravidlo je hotové, když je základní linie prázdná a soubor odstraněn; prázdný seznam výjimek svádí někoho přidat do něj řádek.
Vynucování
majordomus quality report měří a končí kódem 10 při jakémkoli nálezu mimo základní hodnotu. Kontrola rust-quality v .ai/repo/ci/gates.yaml jej spouští, scripts/rust-check jej spouští ve stejném pořadí jako člověk a apps/majordomus-cli/tests/quality.rs tvrdí ratchet v obou směrech z vlastní sady crate. tests/quality.rs nese také negativní fixtury: jeden crate pro každý způsob selhání, každý produkuje svůj vlastní kód a žádný jiný.
Model rozšíření
Nová schopnost potřebuje typovaný vstup a výstup, handler, deklaraci capability! a vedle ní test; projekce následují, jako před tímto rozhodnutím. Nové je, že nový příkaz musí být jedním z nich, nebo říct proč ne, a nová exportovaná položka Rustu musí vysvětlit sama sebe a ukázat se v chodu — nebo přestat být exportována, což je obvykle správná odpověď a je nabídnuta jako náprava v samotném nálezu.