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.

Důkazy

důkaz tvrzení jako zaznamenané provedení, a ne jako cesta k testu, která se přeloží: identita testu odvozená z matice, sedm stavů důkazu a jak se každý odvozuje, proč `proven` a `inputs unchanged` nejsou nikdy jeden stav, tvar knihy a rozhodnutí o její retenci, zaznamenání běhu, čtyři schopnosti s jejich projekcemi, oba směry joinu, kontrola a její ratchet a omezení

Vykresleno z docs/EVIDENCE.md — stejný Markdown, který ukazuje GitHub.

Jak tento repozitář pozná, že je tvrzení dokázáno, a ne jen odkazováno: stabilní identita každého testu odvozená z cesty, kterou tvrzení už jmenuje; zaznamenané provedení nesoucí provenienci, kterou běh sám neměl; ledger posledního provedení na test; a jedno odvození, které obojí spojí a řekne o každém tvrzení přesně to, co lze poctivě říct. Chování tak, jak je implementováno; kde se tento dokument a spustitelný soubor rozcházejí, je dokument špatně a mění se ve stejném commitu. Rozhodnutí je ADR 40; pravidla, kterým slouží, jsou project.no-claim-without-test a project.never-reported-is-not-green pod .ai/repo/rules/project/.

flowchart TD
  claims["docs/CLAIMS.yaml<br>claim → source, implementation,<br>test (a path)"]
  testid["TestId::of(path)<br>suite:84_distribution_model<br>crate:why"]
  ledger[".ai/repo/evidence/ledger.json<br>latest execution per test:<br>outcome, seconds, commit, tree,<br>digest, time, origin, command"]
  latest["ledger.latest(id)"]
  report["evidence::report()"]
  states["proven · inputs_unchanged · stale · failing<br>not_run · unrunnable · no_test"]
  out["CLI · HTTP · MCP · scripts/evidence-check"]
  claims --> testid --> latest
  ledger --> latest
  latest --> report --> states --> out

Problém

docs/CLAIMS.yaml váže tvrzení k testu cestou:

- id: distribution-canonical-model
  claim: Every platform, artifact name and installation URL is derived from one model …
  source: docs/DISTRIBUTION.md
  implementation: apps/majordomus-cli/src/distribution/mod.rs
  test: test/cases/84_distribution_model.sh
  status: guaranteed

Cesta je deklarace, že někde existuje důkaz. Není to důkaz. scripts/generate-site-data --check ověřuje, že se překládá každá cesta source, implementation a test — kontrola odkazu, ne tvrzení. Do tohoto subsystému nikdo nemohl z repozitáře odpovědět, zda ten případ kdy běžel, proti které revizi, s jakým výsledkem, nebo zda se výsledek ještě uplatňuje, a věta se na veřejném webu vykreslovala jako záruka na základě toho, že soubor existuje.

Je to tatáž vada, kterou ADR 30 pojmenovalo o úroveň výš: výsledek testu, který nemůže zastarat, je tvrzení o minulosti vydávané za tvrzení o přítomnosti.

Model

Tři objekty a jedno odvození, v apps/majordomus-cli/src/evidence/.

co to je
TestIdstabilní identita testu: runner, který ho vlastní, a jméno, pod kterým ho tento runner zná
Executionjeden zaznamenaný běh jednoho testu s původem, který běh sám nenesl
Ledgerposlední provedení každého testu, jako sledovaný soubor pod .ai/repo/evidence/
report()spojení: každé tvrzení matice proti každému zaznamenanému provedení, odvozující jedno ProofState na tvrzení

Spojení se odvozuje při každém čtení a nikdy se nezapisuje. Neexistuje druhé místo, kde by se ukládal stav důkazu tvrzení, a tedy ani místo, kde by mohl zastarat.

Identita testu, odvozená, ne zadaná

Identita testu se počítá z cesty, kterou tvrzení už pojmenovává. Nic se nezadává ručně, do matice se nepřidává žádné pole a žádné tvrzení nebylo nutné migrovat:

cesta, kterou tvrzení jmenujeidentityvlastní zdroj testupříkaz, který to spouští
test/cases/84_distribution_model.shsuite:84_distribution_modelstejná cestabash test/run.sh 84_distribution_model
apps/majordomus-cli/tests/why.rscrate:whystejná cestacargo test --test why
cokoli jinéhožádné——

Obě podoby už existovaly: test/run.sh pojmenovává případ podle jména jeho souboru a cargo test --test <stem> spouští integrační binárku podle jejího. TestId::of jen pojmenovává spojení.

„Cokoli jiného“ je odpověď, ne selhání. Tvrzení může jmenovat šablonu, fixture, knihovnu nebo dokument — share/install/install.sh.in, test/lib.sh, vnořenou cestu pod test/cases/, kterou runner nevyčísluje — a zpráva řekne unrunnable, místo aby to počítala jako pokryté. Vlastní zástupný znak matice pro nic, -, v uvozovkách či bez nich, se čte jako žádný test.

Třetí runner by byla varianta Runner a větev v TestId::of. Záměrně jde o změnu kódu a ne konfigurace: runner, který nic nespouští, je runner, jehož výsledky nic nemůže zaznamenat.

Provedení

Každé pole je provenance. Výsledek bez commitu je anonymní zelená, což je jediná věc, z níž se odznak nikdy nesmí odvozovat.

poleco drží
test, runner, sourcekterý test, čí, a kde leží jeho vlastní zdroj
outcomepass, fail, skip, timeout nebo error
secondsjak dlouho to trvalo, celé sekundy
commitúplný název objektu, proti kterému byl běh proveden
working_treeclean, dirty nebo unknown — zda ten commit popisuje to, co běželo
digestsha256:<hex> vlastního zdroje testu, jaký byl v okamžiku zaznamenání běhu
atRFC 3339, UTC, na sekundu
originlocal, ci nebo release
commandpřesný příkaz, který tento jeden test spustí znovu
runCI běh, ve kterém byl zaznamenán — poskytovatel, identifikátor, pokus, workflow, job a adresa — pro záznam ci pořízený uvnitř něj; jinak chybí (viz Zaznamenáno v CI)

Cokoli dokazuje jen pass. Slovo runneru se čte bez ohledu na velikost písmen (ok, pass, passed; fail, failed; skip, skipped; timeout) a cokoli nerozpoznaného je error — výsledek, který nikdo nedokáže klasifikovat, není úspěch. Záměrně neexistuje výsledek not_run: to je nepřítomnost provedení, ne výsledek, který nastal, a jeho zakódování by umožnilo zapisovači napsat „toto nebežel“ a nechat to započítat mezi věci, které běžely.

origin je nezávislé na poskytovateli. Adaptér CI zaznamená ci; nic v modelu neví ani nezajímá, o jaké CI šlo.

Runner mluví tímto slovníkem. Případ, který nemůže splnit předpoklad, volá skip z test/lib.sh, což skončí s exit 4; test/run.sh vypíše skip, zapíše SKIP do TSV v sériové i paralelní fázi a započítá to do třetího součtu vedle prošlých a selhaných. Není to selhání — exit stav běhu se nemění — a ani to není úspěch, čím to bylo, dokud to slovo neexistovalo: případ, který odmítl, skončil s exit 0, runner zapsal ok a kniha zaznamenala důkaz tvrzení z běhu, který nic netvrdil. Stav sám o sobě není deklarací: skip také zapíše soubor, který runner jmenuje v MJ_SKIP_MARK, a případ, který skončí s 4 bez něj — jq -e selže s 4, když nevytvořil žádný výsledek, a případ běží pod set -e — je selháním, ne přeskočením. Tvrzením je a-skipped-case-is-not-a-proof a případem test/cases/413_a_skipped_case_is_not_a_proof.sh, který také odmítne každý případ v test/cases/, který před posledním příkazem stále říká exit 0, takže se starý tvar nemůže vrátit s novým případem.

Sedm stavů

Seřazeno od nejsilnějšího po nejslabší, takže souhrn řazený podle stavu se čte jako žebříček. Každý stav nese v modelu svůj vlastní význam v jedné větě, takže každý povrch říká totéž a nevymýšlí si vlastní výklad.

statestručně
provenúspěšný běh, který předložená revize obsahuje, od té doby se nezměnilo nic než ledger, na čistém stromě na obou koncích
inputs_unchangedúspěšný běh a od té doby se nezměnilo nic, co tvrzení samo jmenuje — nebo se změnilo, ale jeden ze dvou stromů nebyl svým commitem
staleúspěšný běh, který už nedokazuje předloženou revizi: změnilo se něco, co tvrzení pojmenovává, revize ho neobsahuje, git nemohl porovnat, nebo ho zadržuje selhání, které checkout drží
failingposlední provedení testu tohoto tvrzení selhalo, vypršel mu čas nebo jej nešlo spustit
not_runtvrzení pojmenovává test, který vlastní runner, a nebylo zaznamenáno žádné provedení, které by ho spustilo — nikdy nespuštěno, nebo odmítlo běžet
unrunnabletvrzení pojmenovává cestu, kterou v tomto repozitáři neřídí žádný runner, takže nikdy nelze zaznamenat žádné její provedení
no_testtvrzení nejmenuje žádný test — správně pro tvrzení planned nebo rejected, vada pro jakékoli jiné

Každý stav určuje jedna funkce, evidence::freshness, ze zaznamenaného běhu a revize, při které se soudí, a vyhrává první odpovídající řádek. E je commit, na kterém byl běh zaznamenán; „předložená revize“ je ta, která se soudí (viz níže); „vstupy“ jsou to, co tvrzení pojmenovává — přesně tři cesty: jeho source, jeho implementation a vlastní zdroj testu (pravidlo pojmenovává zdroj svého testu a vlastní definici). „Změněno“ je každá cesta, která se mezi E a předloženou revizí liší, a changed' je tato množina bez vlastního řádku ledgeru.

#kdystatereport přidává
1tvrzení nejmenuje žádný testno_test
2jmenuje cestu, kterou žádný runner neprocházíunrunnable
3k testu není nic zaznamenánonot_run
4běh byl přeskočennot_rundetail: test odmítl běžet
5běh selhal, vypršel časový limit nebo skončil chyboufailingdetail: který
6úspěch, a git nemohl porovnat E s předloženou revizístaledetail pojmenovávající E
7úspěch na E, které předložená revize neobsahujestaledetail pojmenovávající E
8úspěch, jehož test už nehashuje na zaznamenaný digest, a žádný vstup se nezměnilstalechanged: test
9prošlo a vstup se změnilstalechanged: tyto vstupy
10úspěch, changed' prázdné, zaznamenaný strom běhu čistý, předložený strom čistýproven
11jako 10, ale zaznamenaný strom běhu byl dirty nebo unknowninputs_unchangeddetail: běh měřil strom, který nebyl jeho commitem (dirty), nebo strom běhu nebyl změřen, takže nelze prokázat, že je jeho commitem (unknown)
12jako 10, ale předložený strom byl dirty nebo unknowninputs_unchangeddetail: předložená revize byla sestavena ze stromu, který nebyl jejím commitem
13průchod, changed' není prázdné a nic z toho není vstupinputs_unchanged
14jako 13, ale route nejmenuje žádné vstupystaledetail: změnu od běhu nelze vyloučit

Jen řádek 10 je proven a jen proven smí být na kterémkoli povrchu vykresleno jako ověřené. Tvrzení stale pojmenovává cesty, které se změnily, nebo ve svém detailu říká, proč je zastaralé, takže čtenář se dozví, co běh zneplatnilo, místo aby byl poslán se podívat. Nevědět není důkaz: nezodpověditelné porovnání je řádek 6, nikdy řádek 13.

Předložená revize. evidence show posuzuje pracovní strom: HEAD plus každou sledovanou, staged i nesledovanou změnu, porovnané s git diff --name-only E -- spolu s nesledovanými soubory, které by git ukázal. evidence show --presented HEAD posuzuje zkontrolovaný commit tak, jak je commitnutý. Commit musí být ten zkontrolovaný — tvrzení, zdroje testů a jejich digesty se čtou z checkoutu, takže verdikt na jakémkoli jiném commitu by smíchal dva stromy — a ledger je ten commitnutý v něm, porovnaný s git diff --name-only E <commit> --. Předložený strom se měří bez pracovní kopie ledgeru, která není tím, co se posuzuje, a --presented-tree může toto měření jen oslabit. Report říká, v čem byl posouzen, v presented. To je čtení, které potřebuje web sestavený z commitu, a žádný povrch o něj zatím nežádá: publikace důkazů (úkol I1955) a sestavení webu, které ji váže na build.json (úkol I1959), jsou pozdější řezy, které předají build.json.commit jako --presented a sestavení s nastaveným build.json.dirty jako --presented-tree dirty. Obě měření se liší: build.json.dirty ignoruje nesledované soubory a předložený strom je počítá. Užší z nich nemůže učinit verdikt proven, protože předložený strom se měří zde bez ohledu na to, co volající deklaruje, a deklarovaný stav ho může jen oslabit.

Necommitnutá provedení pracovní knihy. Pod --presented jsou provedení, která pracovní kopie knihy drží a commitnutá kniha ne, vypsána v presented.uncommitted a čtena jen skrze monotónní pravidlo: doplňkový záznam smí proven odepřít a nikdy neudělit. Když jeden z nich selhal, vypršel nebo skončil chybou, na čistém stromě, na commitu, který obsahuje E a který předložený commit obsahuje, je verdikt proven nebo inputs_unchanged omezen na stale s detailem jmenujícím běh. Strop je stale a ne failing: takový záznam nikdy neurčuje verdikt a necommitnutý průchod ho nikdy nezesílí. Pracovní kniha, kterou nelze přečíst, je odmítnuta, protože běh, který může držet, nelze vyloučit.

Přeskočení není selhání. Test, který odmítl běžet, nic nedokázal a nic neselhal: je not_run s detailem, že odmítl, a záruka, která na něm spočívá, je pojmenována zjištěním, že její test odmítl běžet. Test, který skončil chybou nebo vypršel, neodmítl: je failing.

Obsažení. Průchod zaznamenaný na commitu, který předložená revize neobsahuje, je skutečnost o jiné historii — o větvi, která nebyla nikdy sloučena, nebo o přepsané — i kdyby byl diff mezi oběma stromy sebeprázdnější. Je to stale a jeho detail commit jmenuje. Commit, který tento klon nemá, není ani obsažen, ani neobsažen: git nedokáže odpovědět, což je řádek 6, a nic se proti němu nediffuje. Commit v řádku ledgeru jsou data, takže slovo, které by git četl jako volbu (--output=<file>), se gitu nikdy nepředá: nejmenuje žádný commit a je také řádkem 6.

Agregace. Kde z několika stavů vzniká jeden — pravidlo nad testy, které jmenuje — selhávající část způsobí selhání celku, ať ostatní říkají cokoli, protože pořadí staví failing nad not_run a nejslabší ze selhávajícího a přeskočeného testu by jinak vyšlo jako not_run a selhání by skrylo. Jinak je celek nejslabší z částí, které mohou nést důkaz, a část, kterou nic nikdy nemůže zaznamenat, se počítá jen tehdy, když je jediná.

proven a inputs_unchanged záměrně nejsou stejný stav

V tom je smysl celého subsystému a je to věc, kterou zachovat při jakékoli jeho změně.

proven znamená, že existuje prošlý běh, od té doby se nic nezměnilo, běh měřil commit, ke kterému je připojen a to, co se prezentuje, je vlastní historie toho commitu, jak byla zapsána: prezentovaná revize obsahuje vlastní commit provedení, diff mezi nimi je prázdný kromě ledgeru, working_tree provedení je clean a stejně tak prezentovaný strom. Je to důkaz stromu před vámi — strom, který běh měřil, je strom, na který se díváte.

Každá podmínka je potřeba a každá selhává jinak. Běh zaznamenaný, zatímco čekalo něco jiného, seděl na svém commitu, aniž by ho změřil (ADR 0041: „proven je úspěšný běh zaznamenaný přesně proti tomuto commitu s čistým stromem“), takže je omezen na inputs_unchanged, jakkoli prázdný je diff v okamžiku sestavení hlášení — pozdější git checkout upraveného souboru nemůže zpětně způsobit, že commit popisuje to, co běželo.

Řádek samotné knihy je z toho diffu vyloučen, protože důkazy jsou o stromu, ne součástí toho, co testy měří. Bez tohoto vyloučení by proven bylo z konstrukce nedosažitelné: zaznamenání zapisuje knihu, a tedy špiní strom, a commit záznamu posune HEAD za commit, který záznam jmenuje. To byla skutečná vada v prvním řezu tohoto návrhu, nalezená v recenzi; test/cases/124_evidence.sh nyní asertuje obě poloviny — že strom je po zaznamenání špinavý a že každé tvrzení je přesto proven — takže regrese, která by způsobila, že proven znovu závisí na čistém stromu, by selhala.

Stejné vyloučení platí, když se běh zaznamenává: záznam, jehož jedinou nevyřízenou změnou je řádek předchozího záznamu, se označí clean, protože ten řádek není nic, co by jakýkoli test změřil. Jakákoli jiná nevyřízená cesta je dirty a provedení to říká.

inputs_unchanged znamená, že existuje prošlý běh a od té doby se nepohnulo nic, co tvrzení jmenuje. To je nepřítomnost známé invalidace — ne důkaz na HEAD. Chování mohlo rozbít něco, co tvrzení nejmenuje: sdílená knihovna, kterou implementace volá, schéma, které čte, skript, který případ spouští, zvýšení závislosti, změna samotného test/run.sh. Model závislostí je záměrně mělký a netranzitivní: jsou to tři cesty, které tvrzení uvádí, a nic z nich odvozeného.

Obě alternativy k té volbě jsou horší. Považovat úspěšný běh za platný, dokud se někde něco nezmění, je zelený odznak, jehož odvození nelze prověřit. Prohlásit každý výsledek za zastaralý v okamžiku, kdy se cokoli kdekoli změní, je správné a k ničemu — znamenalo by to, že žádné tvrzení není nikdy dokázáno, kromě vteřin po úplném běhu, v repozitáři, který pushuje do masteru každých pár minut.

Model tedy hlásí, který z obou našel, a slabší označí všude, kde se zobrazuje, jako slabší. Příkazová řádka je vypisuje odlišně a vypíše větu, která vysvětluje druhý. Povrch, který vykreslí oba jako jedno zatržení, znovu zavedl vadu, ať říkají podkladová data cokoli.

Co inputs_unchanged nezachytí, řečeno otevřeně:

  • změna souboru, který tvrzení nejmenuje, a který chování přesto poruší;
  • změna runneru, harnessu, toolchainu nebo prostředí;
  • tvrzení, jehož pole implementation jmenuje jeden soubor tam, kde chování žije v několika;
  • chování, které závisí na něčem zcela mimo repozitář.

Každá z nich je skutečná díra. Odpověď na všechny je táž a není to chytřejší graf závislostí: zaznamenat běh proti HEAD, a stav se stane proven.

Aktuální důkazy pro čtenáře, který počítá trasu jako splněnou

Čtenář, který z verdiktu dělá splněno, nebo ne — prvním je kritérium splnění záměru — si nekreslí vlastní hranici. evidence::current ji kreslí jednou, nad verdiktem pravdivostní tabulky: proven je aktuální a inputs_unchanged je aktuální, jen když běh měřil čistý strom a předložená revize je jeho vlastní (pracovní strom, jehož změny už jsou v porovnání). Řádky 11, 12 a 13 všechny říkají inputs_unchanged a aktuální je jen řádek 13 na dvou čistých stromech: běh zaznamenaný na špinavém stromu není, protože jeho commit nepopisuje, co běželo, a nelze k němu přiřadit žádný pozdější strom. Aktuálnost je vlastnost vstupů, které trasa jmenuje, a zaznamenaného digestu, nikdy rovnost commitu s HEAD, takže commit účetní knihy po běhu nebo změna, kterou trasa nejmenuje, nechá aktuální důkazy aktuální; vše slabší aktuální není.

Kritérium záměru, které jmenuje test, jej posuzuje se zdrojem testu a se zdrojem a implementací každého tvrzení, které tento test dokazuje, jako se svými vstupy (docs/PLANNING.md). Test, který nedokazuje žádné tvrzení jmenující implementaci, nedeklaruje nic o kódu, který testuje, a jeho vlastní soubor tímto kódem není, takže se posuzuje jako cesta, která nejmenuje žádné vstupy (řádek 14): jeho průchod je aktuální, jen dokud se od běhu nezměnilo nic než kniha, a změna testovaného kódu při bajtově shodném souboru testu z něj dělá stale.

Započítání inputs_unchanged jako splněného neznamená, že dva nejsilnější stavy jsou jedna fajfka. met je vlastní otázka záměru a každé kritérium záměru nese proof, verdikt za ním: terminál vypíše current (proven) nebo current (inputs_unchanged) a odznak Cockpitu se čte current · proven nebo current · inputs unchanged v různých barvách. Čtenář, který potřebuje důkaz na revizi, čte proof, nikdy met.

Ledger

.ai/repo/evidence/ledger.json, sledováno, jeden záznam na test: poslední provedení.

Záměrně má tvar základních hodnot měření výkonu pod .ai/repo/benchmarks/ — JSON, commitnutý, zapsaný nástrojem, čtený kontrolou, posuzovaný v diffu. Repozitář už měl precedens pro zaznamenané, commitnuté a přezkoumatelné měření a toto je artefakt téhož druhu ze stejného důvodu, ne něco nového.

JSON, ne YAML vrstvy. Tento repozitář čte záměrně malou podmnožinu YAML (SCHEMAS.md) a kanonický soubor, který umí parsovat jen náš vlastní čtecí kód, je past, do níž už spadl. Kniha je zapisována strojem a čtena strojem, takže je JSON: serde na obou stranách, žádná podmnožina k dodržování a jq na ní funguje.

Poslední na test, žádná historie. Běh na commit pro každý test roste bez mezí a otázka, na kterou ledger odpovídá — je toto tvrzení nyní prokázáno? — čte vždy jen nejnovější. Historie by byla druhou věcí k udržování ve službě otázce, kterou tu nikdo nepoložil; trvalým záznamem toho, co se dělo v čase, je git nad tímto souborem.

Žádný zachycený výstup. Log každého případu jsou megabajty na běh a patří tam, kde běh proběhl — do artefaktu CI, do terminálu. Uchovává se trvalá sémantická část, což je to, co čtenář potřebuje, aby rozhodl, zda výsledku věřit, a jak jej ověřit sám.

Slučováno po testech, nikdy přepisováno po bězích. Zaznamenání jednoho testu nahradí záznam toho testu a každý jiný nechá být. Proto vůbec stojí za to zaznamenávat bash test/run.sh 84_distribution_model: zápis celé knihy by potichu smazal důkazy každého testu, který běh nezahrnoval, a z běhu jednoho případu by udělal repozitář, který dokázal jednu věc — což je horší než ten, který nedokázal nic, protože to vypadá záměrně.

Seřazené a stabilní. Položky jsou seřazeny podle id testu a soubor se zapisuje s odsazením dvěma mezerami a koncovým novým řádkem, takže dva běhy téže množiny vytvoří týž soubor a diff ukazuje, co se změnilo, ne co se přesunulo.

Verzováno a odmítnuto místo uhodnutí. Soubor deklaruje version: 1. Čtenář, který narazí na verzi, již nezná, odmítne s oběma čísly a řekne proč: špatně přečtená kniha je horší než chybějící, protože chybějící hlásí not_run, zatímco špatně přečtená by mohla hlásit úspěch.

Chybějící kniha a prázdná kniha jsou stejná odpověď. Obě hlásí každé tvrzení jako not_run, což je pravda, a ani jedna není chyba — čerstvý klon má první a repozitář uprostřed přijímání má druhou. Souhrn knihy v reportu říká, která to byla, spolu s tím, kolik spuštění obsahuje, s nejnovějším časovým razítkem v ní a s odlišnými commity, vůči nimž byla zaznamenána.

Zaznamenání běhu

Běhy své výsledky už zapisují. test/run.sh zapisuje jeden řádek TSV na případ — name, result, seconds, phase — pokud je nastaveno MJ_TEST_REPORT. cargo test vypisuje řádek Running tests/<name>.rs a řádek test result: na každou integrační binárku. Žádný není trvalý, žádný nenese commit a žádný se k ničemu nepřipojuje.

Běh měří checkout, s nímž odešel, příkazem evidence stamp a evidence record čte, co běh zapsal, vnáší toto měření do každého výsledku s digestem, časem a původem a slučuje je do ledgeru.

MJ_TEST_REPORT=suite.tsv bash test/run.sh
majordomus evidence stamp --producer suite --report suite.tsv --out suite.provenance.json
majordomus evidence record --suite suite.tsv --provenance suite.provenance.json

cargo test --manifest-path apps/majordomus-cli/Cargo.toml 2>&1 | tee crate.log
majordomus evidence stamp --producer crate --report crate.log --out crate.provenance.json
majordomus evidence record --crate-output crate.log --provenance crate.provenance.json

Poté commitněte ledger. Záznam, který není commitnut, je lokální názor.

Zaznamenává; nerozhoduje nic. Případ, který selhal, je případ, o kterém runner řekl, že selhal, a test, který není v žádné zprávě, zůstává přesně tak, jak byl — zpráva o něm řekne not_run, což je pravda.

Běh měří; zapisovač přenáší. Zapisovač, který by každý výsledek opatřil stromem, který vidí, by ručil za strom, který neviděl: CI zaznamenává v čerstvém checkoutu a lokální běh se může zaznamenat poté, co se jeho checkout změnil. Proto evidence stamp měří checkout tak, jak jej běh zanechal: commit; strom, s ignorovanou knihou důkazů a vyloučenými vlastními nesledovanými výstupy běhu (--exclude jmenuje soubor nebo adresář, nikdy vzor, celý checkout ani cestu držící sledované soubory, takže výjimka nikdy neskryje sledovanou změnu; zpráva uvnitř checkoutu je jedním z těch výstupů); producenta, jeho toolchain, verzi zapisovače, digest zprávy, hostitele a běh CI. evidence record --provenance [<producer>=]<file> přenese toto měření do provádění zprávy. Když byl měřený strom čistý, digest testu se bere z commitu, který drží bajty, jež běh provedl. Zpráva zaznamenaná bez měření nese strom unknown, který se nikdy nečte proven. Lokální nebo release měření musí pojmenovat svou zprávu (--report), jinak neřekne, který běh změřilo. Razítko měří checkout v okamžiku pořízení, po zapsání zprávy, a ne strom, zatímco běh probíhal: běh na špinavém stromě, který se před razítkem uklidí, se čte jako čistý.

Odmítnutí, všechna záměrná:

  • není co zaznamenat — nebyl zadán žádný --suite, --crate-output ani --coverage;
  • není pracovní strom gitu s commitem — běh tam zaznamenaný by nenesl žádný původ a nic by nedokazoval;
  • chybně formovaný řádek reportu — odmítnut místo přeskočení, protože tiše zahozený případ nechá tvrzení hlásit not_run po běhu, který ho spustil, což je lež v bezpečném směru, ale pořád lež;
  • výsledek testu, který tento checkout nemá — jmenovaný v seznamu unknown záznamu místo zaznamenání nebo zahození, protože znamená, že se runner a matice rozešly;
  • měření jiného commitu — zpráva běžela jinde: zaznamenejte ji v checkoutu commitu, na kterém běžela;
  • měření jiné zprávy — její otisk není otiskem zprávy;
  • měření pro zprávu, která nebyla zadána, nebo klíčované na jiného producenta;
  • lokální měření nebo měření vydání, které nejmenuje žádný report;
  • CI report bez měření — záznam CI nese měření, které provedl jeho vlastní job. Soubor stromu jobu, který žádný report nejmenuje, se pro záznam CI přijme, dokud CI neoznačí svým reportem.

Každé zaznamenání je zároveň typovaný běh. evidence record vrací EvidenceRunRecord (jeho id, původ, commit, nejslabší strom přes zadané reporty, měření každého reportu, součty podle výsledku a runneru, co chybělo, bylo vynecháno nebo neznámé, provedení) a zapisuje ho přes --run-record <file>. --coverage <summary> připojí souhrn, který zapsal scripts/rust-coverage --summary-json, svázaný s commitem a stromem, které jeho běh měřil, spolu s minimy, která uvádějí soubory prahů; je aktuální jen pro svůj vlastní commit a jen na čistých stromech. --ledger local se slučuje do .ai/local/evidence/ledger.json, které repozitář ignoruje: nemění žádný sledovaný soubor a nehýbe žádným verdiktem. Záznam běhu nikdy verdikt nerozhoduje; pozdější povrch dovolí oslabit verdikt jen jediným monotónním pravidlem (freshness::weakened_by, k němuž záznamy běhů dosahují přes evidence::weakened_by_records), které může proven odepřít, ale nikdy udělit.

Binárka cratu uvádí jen to, co běželo. cargo test vypíše ok na výsledkovém řádku binárky, která nespustila vůbec nic, takže recorder čte počty, nejen slovo: binárka se selhávajícím testem je selhání, ať její slovo říká cokoli; ta, která nic neprošla (každý test ignorován, nebo žádné nejsou), nebo spustila jen podmnožinu, kterou vybral filtr názvu, je skip, absence důkazu, ne důkaz; ta, jejíž výsledkový řádek nenese počet, nebo která před další binárkou nevypsala žádný výsledkový řádek, je error, protože harness nemohl říct, co běželo. Barevné kódy se z každého řádku odstraní před párováním, takže barevný běh se čte jako prostý. Co výstup obsahoval a žádné tvrzení to zatím nemůže jmenovat, je vypsáno, s důvodem, v seznamu dropped záznamu (řádek dropped v textovém výstupu) místo ignorování: vlastní binárka unit testů cratu (Running unittests src/lib.rs), jeho doctesty (Doc-tests <crate>), binárka mimo tests/ a řádek test result: bez řádku Running před ním, který se nikdy nepřičítá předchozí binárce.

V CI

Polovina zapojení už existuje. Úloha suite v .github/workflows/validate.yml spouští bash test/run.sh s MJ_TEST_REPORT: suite.tsv a nahrává to TSV jako artefakt ci-metrics-suite; úloha macos dělá totéž. Krok záznamu je jeden řádek za krokem sady:

- if: always()
  run: majordomus evidence record --suite suite.tsv --provenance suite=suite-tree.json --origin ci

Co není zapojeno a je cílem, ne popisem: dnes ten krok nespouští nic a ledger zapsaný v checkoutu CI se zahodí s runnerem. Dostat důkazy CI do repozitáře vyžaduje commit, který běh pull requestu dělat nesmí; tvar, který funguje, je úloha push-to-master, která ledger zaznamená a commitne vlastním commitem, tak jako se zapisuje baseline měření výkonu. Dokud to neexistuje, ledger zapisuje a commituje ten, kdo sadu spustí lokálně, a stavy jsou o výsledku poctivé tak či tak.

Schopnosti

Jedna deklarace v src/capability/builtin/evidence.rs; z ní je odvozen každý povrch (CAPABILITIES.md).

schopnostpříkazová řádkaHTTPMCP
evidence.reportmajordomus evidence showGET /api/v1/evidencenástroj majordomus_evidence, zdroj majordomus://evidence
evidence.claimmajordomus evidence claim <id>GET /api/v1/evidence/claimnástroj majordomus_evidence_claim
evidence.testmajordomus evidence proves <id>GET /api/v1/evidence/testnástroj majordomus_evidence_test
evidence.recordmajordomus evidence record——
evidence.stampmajordomus evidence stamp——

Těchto pět jsou podpříkazy Rust programu a majordomus je jediný program, který instalovaný nástroj dává na PATH. Shellový nástroj má příkaz stejného jména, který zaznamenává, co úloha dluží (majordomus evidence --covers <token>, --gate <id>; CLI.md), a bere jen volby. Slovo na první pozici tedy nikdy nepatří shellovému nástroji: launcher předá show, claim, proves, record a stamp programu s každým argumentem beze změny, včetně globálního --repo na kterékoli straně příkazu, a exit kód je pak kódem programu. Která slova to jsou, se čte z vlastní deklarace programu, nikdy se nevypisuje v launcheru, a majordomus evidence --help vypíše obě poloviny. Až do 0.22.0 se slovo dostalo k parseru voleb, který odpověděl evidence: unknown option record; případ 2998_evidence_reaches_the_subcommands_the_executable_owns drží každý řádek, který tato stránka vypisuje, proti launcheru, z checkoutu i z instalovaného uspořádání.

evidence show bere --state, --status a --findings jako filtry a --check, které končí 10, když některé tvrzení deklaruje záruku, kterou důkazy nepodporují. Součty se počítají před filtrem: filtrovaná odpověď říká, jak velkou část matice prozkoumala, nikdy kolik jí vrátila.

Žádný z nich nekešuje. Ledger je soubor, který se mění mimo proces, a keš by byla přesně ten zastaralý důkaz, který tento subsystém existuje, aby pojmenoval.

evidence.record je jediný, který zapisuje, a po síti není dosažitelný. Deklaruje příkazovou řádku a nic jiného. Povrchy MCP a HTTP tohoto programu jsou jen pro čtení — vlastnost serveru, ne náhoda toho, co bylo sestaveno — takže zapisovač je schopnost vývojáře: nabízí se tomu, kdo program spouští, a nikomu přes socket. Je deklarován jako schopnost, a ne ručně psán jako příkaz, aby zůstal uvnitř registru, grafu příkazů a generované reference. Je také prominut z měření se zaznamenaným důvodem: měřit ho ve smyčce by přepisovalo důkazy repozitáře.

evidence.stamp nic nezapisuje a je také jen pro příkazovou řádku: čte zprávu, jejíž cestu uvede volající, a síťový klient nesmí volit soubor, který tento spustitelný soubor čte.

Vztah, po kterém lze jít jen jedním směrem, je poloviční vztah.

majordomus evidence claim distribution-canonical-model
majordomus evidence proves suite:84_distribution_model
majordomus evidence proves test/cases/84_distribution_model.sh   # the path resolves to the same test

evidence claim odpovídá na otázku co toto tvrzení dokazuje: stav a jeho význam, provedení za ním, cesty, které se od té doby změnily, příkaz, který jej reprodukuje, a další tvrzení, která dokazuje tentýž test — test sdílený dvanácti tvrzeními je test, jehož selhání je dvanáct zjištění, a čtenář jednoho by měl vidět ostatních jedenáct bez hledání.

evidence proves odpovídá na co tento test dokazuje: jeho poslední provedení, zda jeho zdroj stále hashuje na to, co provedení zaznamenalo, příkaz, který ho spustí znovu, a každé tvrzení, které ho jmenuje, se stavem, v němž je.

Oba čtou stejné odvození, takže se oba směry nemohou rozcházet. Tvrzení, které matice nedeklaruje, je not-found, ne prázdná odpověď: překlep čtený jako „toto tvrzení nemá žádné důkazy“ je jediná odpověď, kterou tyto schopnosti nesmí nikdy dát.

Subjekty: čeho se verdikt týká

Tvrzení je jedna věc, na kterou se čtenář ptá, a ne jediná. Čtenář se ptá také, zda je funkce prokázána, co testuje příkaz, co dokazuje pravidlo. Každá z nich je předmět: jedna deklarace, kterou repozitář už činí, pojmenovaná klíčem <kind>:<id> — claim:<id>, rule:<id> (id pravidla bez verze), feature:<id>, command:<path> (slova příkazu spojená jednou mezerou, command:commit plan), capability:<id>, mcp:<tool> a use_case:<id>. Index předmětů říká u každého z nich, z jakých předmětů se skládá a které testy dosahuje; nic z toho se neuvádí ručně.

DruhVlastní routyJejí členové
claimtest, který tvrzení jmenuje, posuzovaný podle zdroje tvrzení, implementace a zdroje testužádné
pravidlokaždá cesta, kterou jmenuje její vynucovací blok a kterou vede runner; jakákoli jiná jmenovaná cesta se vypíše jako mechanismusžádné
commandpřípady, jejichž první řádek covers ho jmenuje (behaviour) a jejichž první negativní řádek ho jmenuje (negative); pro dokumentovanou příkladovou cestu binárka, která spouští každý příklad, existuje-ližádné
schopnostpřípady, jejichž první řádek covers nebo negative jmenuje capability:<id>jeho cesta v příkazové řádce a každé tvrzení implementované v souboru, v němž je složen jeho modul
mcpžádnéschopnost, která nástroj deklaruje a jejímž je aliasem
případ užitíjeho scénář, který kniha nezaznamenávápříkazy, které spouští, pravidla, která uplatňuje, tvrzení, která dokládá, a nástroje MCP, které volá
funkcežádnétvrzení, pravidla, příkazy a případy užití, které jmenuje

Příkazové slovo je veřejný příkaz shellového nástroje, dokumentovaná příkladová cesta spustitelného souboru nebo cesta schopnosti v příkazové řádce a předmět jmenuje programy, kterým patří. To jsou slova grafu příkazů (majordomus commands graph): každý příkaz spustitelného souboru, který lze spustit, nese dokumentovaný příklad a skupina, která jen drží příkazy, žádný, takže skupina jako evidence není slovem spustitelného souboru, a případ drží předměty příkazů a jejich programy rovné grafu. Slovo, na něž odpovídají oba programy, je jeden předmět jmenující oba, s poradním nálezem command_in_two_programs: jeho trasy v hlavičce dokazují shellový nástroj a jeho příkladová trasa dokazuje spustitelný soubor. Členové tvoří graf bez cyklů — případ užití nikdy nezahrnuje funkce, které ho jmenují, a nic nezahrnuje funkci — a odkaz, který se nerozřeší na nic, se tu přeskočí a ohlásí ho validace produktu.

Verdikt subjektu je jedna agregace, kterou už používají tvrzení a pravidla, nad jeho částmi: selhávající část činí subjekt selhávajícím; jinak je verdikt nejslabší z částí, které mohou nést důkaz; jinak nejslabší ze všech jeho částí; a subjekt bez části se čte no_test. Součty jeho částí podle stavu jsou vždy vedle verdiktu, takže jedna část not_run není za ním nikdy skryta. Subjekt se posuzuje podle svých vlastních routes a vlastních verdiktů svých členů, nikdy podle syrových routes členů. Posuzuje se na předložené revizi, jako evidence show --presented posuzuje tvrzení: route tvrzení je důkaz spojení tvrzení, routes příkazu procházejí toutéž pravdivostní tabulkou a týmž monotónním pravidlem nad necommitnutým během pracovního ledgeru a zpráva o pravidlech, která posuzuje pracovní strom, je omezena předloženým stromem, takže proven odvozené z pravidla se stane inputs unchanged, když předložený strom není jeho commitem, jako každá jiná route. Plocha, která čte záznamy nad rámec sledovaného ledgeru, smí route zeslabit, než se cokoli agreguje, a nikdy ji nezesílí: route si ponechá slabší ze dvou stavů.

Předmět pravidla čte stav důkazu pravidla jedním deklarovaným mapováním:

Stav pravidlaStav důkazůMůže nést důkaz
proven, inputs_unchanged, stale, failing, not_run, unrunnablestejné slovoano
gatednot_run: kontrola odmítá porušení a žádný zaznamenaný běh chování neprokazujeano
reviewedno_testne
unprovenno_testjen pro blokující pravidlo, u něhož je nejmenování ničeho vadou
danglingunrunnable a subjekt z něj složený nese zjištění dangling_memberano

Scénář a mechanismus se vypisují, aby je čtenář viděl, a ani jeden nikdy nerozhoduje verdikt: kniha nedrží žádný běh scénáře a mechanismus je kontrola nebo skript, který odmítá porušení, ne běh, který prošel. Funkce, jejíž členové nedosáhnou na žádný test, který runner řídí, nese zjištění feature_without_evidence. Zjištění subjektů jsou doporučující, dokud je nedrží řez kontroly.

Commitnutý index, site/data/registry/evidence-subjects.json, zapisuje majordomus generate site a nese jen strukturu: žádný řádek knihy, žádný commit a žádný čas, takže zaznamenání běhu ho nikdy nezestárne, a generate --check odmítne kopii, která se od odvození liší. Shell a jq jej jen spojují. Nahrazuje nebo se rovná dřívějším shellovým odvozením od předmětu k testu:

  • stránky příkazů (scripts/generate-site-data) čtou první řádek covers a negative každého případu, jako index, a případ drží obojí shodné, dokud se stránky nepřipojí k indexu;
  • doktrína pokrytí příkazů (lib/commands.sh) čte stejný první řádek;
  • kontrola command-furnished a stopa dopadu případů užití shodují příkaz kdekoli v jakémkoli řádku covers mezi hranicemi slov, takže by četly druhé záhlaví téhož druhu i příkaz uvnitř delšího slova (capability:commit.plan drží plan); případ drží každý případ na jednom záhlaví každého druhu a jeho řádek covers na jmenování týchž veřejných příkazů při obou čteních, takže se zatím nemohou rozejít; kus kontroly je přesune na index;
  • stránky schopností (scripts/lib/executable-site.jq) dávají každé schopnosti tvrzení implementovaná v souboru jejího modulu a případ je drží shodná s indexem;
  • stránky doktríny pojmenovávají první test každého pravidla a případ drží každý z nich u trasy nebo mechanismu toho pravidla.
jq '.subjects["feature:evidence"]' site/data/registry/evidence-subjects.json
majordomus generate site --check

ADR 0087 (navrženo) zaznamenává rozhodnutí indexovat důkazy podle subjektu.

Kontrola

scripts/evidence-check vykresluje vlastní odpověď spustitelného souboru a hlídá ji ratchetem. Sám nic neodvozuje — kontrola s druhým názorem na to, co je prokázáno, by byla druhým modelem důkazu.

scripts/evidence-check              # report; 0 clean, 10 with regressions
scripts/evidence-check --json       # the report, from the capability itself
scripts/evidence-check --strict     # every unsupported guarantee fails; the baseline is ignored
scripts/evidence-check --baseline   # rewrite the baseline from what is found now
scripts/evidence-check --repo PATH  # judge that repository instead of this one

Posuzují se jen tvrzení guaranteed. advisory uvádí, že vynucování není zvenčí pozorovatelné, planned, že ho nic neimplementuje, a rejected, že ho nic implementovat nebude; požadovat u nich aktuální důkaz by znamenalo požadovat důkaz věci, o které tvrzení už říká, že tam není. Tvrzení guaranteed podporuje proven nebo inputs_unchanged a nic jiného; stale, failing, not_run, unrunnable a no_test jsou zjištění, každé s důvodem a příkazem, který by ho vyřešil.

stale je ta z nich, která vypadá jako podpora a není jí, a počítala se za podporu, dokud se nezměřila: 14 záruk na ní stálo, proti 0 dokázaným. Zastaralý běh je průchod — říká to passing() a shrnutí jej musí držet odděleně od selhání — ale je to průchod subjektu, který se od té doby pohnul: implementace tvrzení nebo jeho test se změnily po běhu nabízeném jako jeho důkaz, takže to, co prošlo, není to, co tvrzení nyní jmenuje. Jeho přijetí způsobilo, že kontrola přijala jako podporu záruky měření něčeho jiného, což je přesně ta vada, kterou tento subsystém existuje pojmenovat o úroveň níž. Spodní mez je inputs_unchanged: slabší než proven, ale aspoň průchod tohoto subjektu.

Dnes je poradní a je to volba s koncem. Kniha začíná prázdná, takže v den, kdy kontrola přijde, je každé zaručené tvrzení not_run — pravda, a jako blokující kontrola by to znamenalo, že nikdo nemůže nic mergnout, dokud se nezaznamená běh celé sady. Dluh, který v tu chvíli existoval, tedy zapíše .ai/repo/evidence-baseline.txt příkaz --baseline, nikdy ruka.

To, co ratchet odmítá, není členství v onom seznamu. Bylo a byl to defekt, ne přísnost: tvrzení napsané po baseline v ní chybí z jediného důvodu, z jakého může tvrzení chybět ve snímku minulosti — ještě neexistovalo — a kontrola hlásila každé takové tvrzení jako takové, které „ztratilo důkazy, jež je podporovaly“, důkazy, které nikdy nemělo. Každá nově mergnutá záruka zčervenala trunk pro každou větev, která mergovala po ní. Ratchet, jehož poplach se spouští při růstu místo při regresi, měří špatnou věc: zdaňuje psaní tvrzení, což je přesně to, co matice existuje, aby odměňovala.

Základ tedy zaznamenává stav na tvrzení — obě poloviny, řádek + pro záruku, kterou běh podpořil, a holý řádek pro tu, kterou žádný běh nepodpořil — a odmítají se dvě věci:

odmítnutoco to jeproč ho členství nevidělo
ztracenotvrzení, které baseline zaznamenala jako podporované a které důkazy už nepodporujítvrzení, které ztratí důkaz, se připojí k nepodpořeným přesně tak, jako nově napsané tvrzení
staženopodporovaná záruka, která už vůbec není zaručeným tvrzením maticenic se nestalo nedokázaným; jmenovatel se zmenšil, což je způsob, jak ratchet hnije, aniž by kdy zčervenal

A jedna věc se záměrně neodmítá: zaručené tvrzení, které základní stav neznal a které přichází bez zaznamenaného běhu. To je nový dluh — hlášený, započítaný a nikoli regrese — protože jeho odmítnutí je výše popsané chybné chování. Do základního stavu se přijímá pomocí --baseline, ve vlastním commitu, s důvodem, a odtud ho ratchet drží. Omezení je řečeno, ne skryto: tvrzení povýšené z advisory na guaranteed bez běhu je rovněž nový dluh a také se pouze hlásí.

Stále se zpřísňuje jen jedním směrem. Nepodporovaná polovina se zmenšuje zaznamenáním běhu, který tvrzení prokazuje, a přepsáním souboru — nikdy přidáním řádku. --strict odmítne každou nepodporovanou záruku; je to konečný stav a CI na něj přepne, až bude nepodporovaná polovina prázdná.

Kontrola je pojmenována v .ai/repo/ci/gates.yaml jako evidence-check v úloze structure s always: true — takže verdikt přichází při každém pushi, a ne až si někdo vzpomene ho spustit, což je druhá polovina project.never-reported-is-not-green. Třída cest evidence pojmenovává, co může změnit odpověď: ledger, baseline, skript kontroly, matici tvrzení, generované stránky tvrzení a tento dokument.

proven je tu vzácné, a to je model, který funguje

Ledger je sledovaný soubor a zaznamenávání ho zapisuje do stromu. Záznam pořízený na HEAD tedy zanechá strom špinavý a commitnutí ledgeru posune HEAD za commit, který provedení jmenují — takže pravidlo, které by žádalo čistý strom na HEAD, by zde nebylo nikdy splněno.

Proto je pravidlem diff a ne HEAD a proto je z něj vyloučen řádek samotné účetní knihy. proven je dosažitelné v repozitáři, který sleduje vlastní důkazy, což je jediné uspořádání, v němž mají důkazy cenu pro někoho jiného než pro toho, kdo spustil testy. test/cases/124_evidence.sh to dokazuje na fixture, jejíž účetní kniha je sledována přesně jako v tomto repozitáři.

Zaznamenáno v CI

Kniha výše drží vše, co bylo zaznamenáno do stromu. Běhy CI se zaznamenávají také a validační běh své řádky nikdy necommituje: uchovají se tam, kde běh proběhl, jako záznam běhu. Řádky běhu na trunku se commitují později zvláštním pull requestem (ADR 0080), protože běh nemůže commitnout, co dokázal, aniž by přidal commit za ten, který dokázal, na trunku, který se pohybuje rychleji, než sada doběhne. Tento zaznamenávací pull request ještě není zapojen. Rozhodnutí je ADR 68, jak ho mění ADR 0087.

flowchart LR
  suite["suite job, four shards<br>suite.tsv · suite-tree.json, joined"]
  crate["rust job, three lanes<br>cargo-test-1..3.txt · crate-tree.json, joined"]
  cov["coverage job<br>coverage.json"]
  collect["evidence job<br>scripts/ci/evidence-collect"]
  artifact["artifact `evidence`<br>report · ledger · coverage · manifest"]
  pages["pages.yml<br>scripts/pages evidence"]
  site["/evidence/<br>current · stale · unknown · unavailable"]
  suite --> collect
  crate --> collect
  cov --> collect
  collect --> artifact --> pages --> site
  collect -. "commit still the tip: dispatch" .-> pages

Co kolektor odvodí jako první. Než cokoli zaznamená, scripts/ci/evidence-collect změří checkout, ve kterém běží, a odvodí report přes majordomus evidence show, jako report.txt a report.json. Reporty, které dostane, a adresář, do kterého zapisuje, jsou pod dočasným adresářem runneru, mimo tento checkout. Report je tedy verdiktem sledované knihy na commitu běhu, odpovědí, kterou dá čistý checkout tohoto commitu, a report_tree manifestu říká, zda byl checkout při odvození čistý.

Čím jsou řádky běhu. Teprve poté zaznamená výsledky sady a cratu přes majordomus evidence record --origin ci a ponechá knihu, která je nyní drží, jako ledger.json. Tyto řádky jsou záznam běhu. Počítají se v manifestu a nerozhodují o žádném verdiktu, v artefaktu ani na webu. Řádky běhu trunku se stanou verdikty, jen když je do sledované knihy přistane záznamový pull request z ADR 0080. Kolektor také sumarizuje export pokrytí přes scripts/rust-coverage --summary-json.

Co měří producenti a zapisovatel. Každá úloha, která spustila testy, změří svůj vlastní checkout hned po svém běhu a předá měření vedle své zprávy: suite-tree.json z úlohy suite, crate-tree.json z úlohy rust. Každá jmenovitě vylučuje výstupy, které jmenuje její vlastní běh, na cestách, kam je ten běh zapisuje, a nic jiného: suite svou zprávu v kořeni; úloha rust svá časování a adresář artefaktů, který scripts/rust-check zapisuje v adresáři crate, kde běží. Měření uvádí, co vyloučilo. Zapisovatel odmítne zprávu CI bez měření, které provedla její vlastní úloha, takže sběratel předává soubor stromu každé úlohy jako provenienci té zprávy a řádky každého runneru nesou měření vlastní úlohy (manifest drží stromy řádků jako rows_working_tree). totals manifestu, jeho working_tree (nejslabší strom přes zaznamenané zprávy) a slova suite a crate v absent se čtou ze záznamu běhu zapisovače, ne počítají podruhé; nikdy se nečtou z checkoutu zapisovače, který je z konstrukce čistý. Zpráva, jejíž úloha změřila jiný commit než ten, na kterém běží sběratel, nebo jejíž úloha nezanechala žádné měření, se odmítne: nezaznamená se a pojmenuje se.

Kolektor zapisuje manifest.json a uvádí:

  • commit, který běžel (u pull requestu merge commit GitHubu), a head_sha, head, z něhož byl postaven, nesený vedle něj a nikdy místo něj;
  • událost a běh;
  • výsledky provedení tohoto běhu;
  • měření producentů, working_tree záznamu běhu a report_tree;
  • které zprávy chyběly, včetně zprávy, která nedala žádné provedení, a které byly odmítnuty, s důvodem.

Chybějící zpráva se jmenuje, nikdy se nepočítá jako úspěch. Úloha ponechává adresář jako artefakt evidence. Není to kontrola: čte úlohy, které už rozhodly, a červená sada je přesně ten důkaz, který stojí za uchování.

Provedení jmenuje svůj běh. Záznam ci pořízený uvnitř prostředí GitHub Actions označí každé provedení run: poskytovatel, identifikátor běhu, pokus, workflow, job a adresa, kterou poskytovatel dal. RunRef::from_env je jediné místo, které zná názvy prostředí. Lokální záznam nejmenuje žádný běh, ani uvnitř shellu CI, a řádky zaznamenané před pojmenováním běhů nemají run vůbec.

"run": {
  "provider": "github_actions",
  "id": "<run id>",
  "attempt": 1,
  "workflow": "validate",
  "job": "evidence",
  "url": "https://github.com/korczis/prismatic-majordomus/actions/runs/<run id>/attempts/1"
}

Co o tom říká publikace. Před sestavením scripts/pages evidence najde artefakt evidence zaznamenaný k publikovanému commitu nebo jeho nejbližšímu předku. Nejbližšího předka vybírá podle historie, ne podle nejnovějšího nahrání. Zapíše site/data/evidence.json, které se nikdy nekomituje. Report publikuje tak, jak byl odvozen, a manifest čte jen proto, aby rozhodl, zda ho běh potvrzuje: důvod může current zadržet, ale report nikdy nezmění. ADR 0087 definuje stavy, které říká:

statekdyco stránka říká
aktuálníběh se týká publikovaného commitu, každá úloha, která spouštěla testy, měřila čistý strom, zpráva byla odvozena na čistém checkoutu a je přiložena a nic nechybělo, nebylo odmítnuto ani neselhaloCURRENT, s commitem a během
zastaraléběh je z předka, nebo je z publikovaného commitu a zaznamenal selháníSTALE, s počtem commitů a změněných souborů mezi, nebo se selháními
neznáméběh je ze zveřejněného commitu a nemůže zprávu potvrdit: strom neměřený nebo ne čistý, zpráva chybějící nebo odmítnutá, nebo její vlastní zpráva neodvozená či nepřenesenáUNKNOWN, se všemi důvody
nedostupnéžádný uchovaný artefakt této historie, nebo takový, který nelze přečístUNKNOWN, s důvodem

Cokoli zadržuje current, je vypsáno pod větou. Žádný ze stavů publikaci nezastaví. Zastaví ji snímek UI ukazující testy jako nespuštěné (I2786):

  • Účetní kniha běhu, na místě. Úloha snímku nejdřív vloží ledger.json téhož artefaktu do cesty účetní knihy checkoutu (--ledger-out), takže obrazovky posuzují řádky běhu u publikovaného commitu. Jde o dodatek ADR 0087 z 2026-10-17, dokud neexistuje větev pro záznam.
  • Kontrola. scripts/pages published-evidence se pak zeptá evidence.report a odmítne s exitem 10, když je tvrzení s not run víc, než deklaruje .ai/repo/evidence/published-not-run.txt. Pravidlo project.published-evidence-is-what-ci-recorded to drží. Když se důkazy běhu masteru uchovají, zatímco jeho commit je stále špička, job odešle pages.yml a tato publikace říká aktuální, když běh potvrdí report. Když master pokročil, novější publikace už nese důkazy jako zastaralé a novější běh je obnoví.
scripts/pages evidence                          # this commit, fetched with gh
scripts/pages evidence --from <dir> --out FILE  # a gathered directory, offline
scripts/pages evidence --from <dir> --out FILE --ledger-out .ai/repo/evidence/ledger.json  # and its ledger, in place
scripts/pages published-evidence --bin <majordomus>  # refuse a UI that would show tests as not run
cat cargo-test-*.txt > cargo-test.txt   # the rust job's three lanes, joined
scripts/ci/evidence-collect --out <dir> --suite suite.tsv --suite-tree suite-tree.json \
  --crate-output cargo-test.txt --crate-tree crate-tree.json --coverage coverage.json

Behaviorální důkaz je test/cases/357_ci_records_evidence.sh.

Čím toto není

Není odolný proti manipulaci a netváří se, že je. Ledger je sledovaný soubor. Osoba do něj může zapsat "outcome": "pass" a reviewer uvidí diff. Zaznamenaný digest přináší detekci zastaralosti, která přežije revert — provedení, jehož zdroj testu už nemá zaznamenaný digest, nespustilo test, který tam je nyní, ať říká commit cokoli. Kryptografický ceremoniál proti někomu, kdo už může do repozitáře commitovat, by byl ceremoniálem bez modelu hrozeb. Poctivou bezpečnostní vlastností je ta, o niž se tento repozitář opírá všude jinde: padělaný výsledek je revidovatelný řádek v commitu, nikoli nevystopovatelná zelená.

Všimněte si přesného dosahu toho digestu: hlásí jej evidence proves jako digest_matches a není vstupem stavu důkazu, o kterém rozhoduje porovnání v gitu. Strom, kde se obě neshodují, je strom, kde bylo něco zaznamenáno proti commitu, který ho nepopisuje, a důvodem je obvykle pole working_tree: dirty.

Není to řídicí rovina provádění. src/execution/ a EXECUTIONS.md se týkají provádění schopností běžícího serveru — rozdělané práce, průběhu, typovaných událostí, které zanikají s procesem. Tohle se týká běhů testů: trvalých, commitnutých a čtených dlouho poté, co proces, který je vytvořil, skončil. Dva různé subjekty, které sdílejí jedno anglické slovo.

Není to důkaz závazků z lib/evidence.sh. Ten podsystém (ADR 30) je o tom, co úloha dluží, než ji lze označit za dokončenou, a jeho kniha je přírůstková kniha úloh. Obojí sdílí jednu myšlenku — důkazy, které mohou zastarat — a nic jiného. Žádné nečte záznamy toho druhého.

Neposuzuje výsledky. Nic zde znovu nespouští test, znovu neinterpretuje výsledek ani nerozhoduje, že selhání bylo způsobeno prostředím. Zapisovač zaznamenává, co řekl runner.

Neučiní tvrzení pravdivým. Tvrzení se stavem proven je tvrzení, jehož test proti tomuto commitu prošel. Zda test tvrzení opravdu testuje, je otázka pro revizi a project.no-claim-without-test je pravidlo, které ji drží.

Když něco selže

zprávavýznamnáprava
nothing to record: give --suite, --crate-output, or bothrecorder byl vyvolán bez vstupupojmenujte report, který běh zapsal
this is not a git work tree with a commit, so a run recorded here would carry no provenancezáznam mimo repozitář s commitemzáznam v checkoutu, proti kterému byl běh proveden
line N of the run report has M field(s); it is name<TAB>result<TAB>seconds<TAB>phasepoškozený řádek TSV; nic se nezapíšereport znovu vygenerujte pomocí MJ_TEST_REPORT, ručně ho neupravujte
unknown suite:99_ghost (no such test here)běh jmenoval test, který tento checkout nemárunner a matice se rozešly; zjistěte, který se pohnul
… declares version N and this executable reads version 1kniha tvaru, který tento build neznápoužijte spustitelný soubor té generace, nebo soubor záměrně zmigrujte
… is not a ledger this version can read: <serde error>kniha není platný JSON tohoto tvaruopravte ji nebo smažte; smazaná kniha hlásí not_run, což je poctivé
`x` is not a claim of docs/CLAIMS.yamlid tvrzení, které neexistujemajordomus evidence show vypíše každé id
`x` names no testargument pro evidence proves, který není ani identita, ani cesta k testuzadejte suite:<case>, crate:<binary> nebo cestu
evidence-check: no majordomus executable (kód 12)kontrola nenašla buildsestavte crate, nebo nastavte MAJORDOMUS_BIN
evidence-check: … is absent; run scripts/evidence-check --baseline (kód 12)ratchet nemá baselinenapište jednu, ve vlastním commitu, s důvodem
evidence-check: N guaranteed claim(s) lost the evidence that supported them (kód ukončení 10)tvrzení, které základní stav zaznamenal jako podporované, už neníspustit pojmenovaný test a zaznamenat ho, nebo opravit, co se rozbilo
evidence-check: N supported guarantee(s) left the matrix (kód ukončení 10)prokázaná záruka byla smazána nebo přejmenována, takže repozitář prokazuje méně, než zaznamenává základobnovte tvrzení, nebo přepište základ ve vlastním commitu s vysvětlením, proč záruka zmizela
evidence-check: N guaranteed claim(s) the baseline does not knownový dluh: tvrzení napsané po základním stavu, bez zaznamenaného běhu — hlásí se, nikdy nezpůsobí selhánízaznamenat běh, nebo scripts/evidence-check --baseline ve vlastním commitu
evidence-check: N baseline line(s) now supported — tighten the baselinedluh byl splacen a seznam ho nenásledovalscripts/evidence-check --baseline, ve vlastním commitu

Stabilita

status
identita odvozená z cesty případu a cesty testu crate; cesta, kterou nevede žádný runner, nejmenuje žádný testimplementováno, unit testy v src/evidence/mod.rs
cokoli dokazuje jen rozpoznaný průchod; nerozpoznané slovo výsledku je chybaimplementováno, unit testy v src/evidence/mod.rs
sedm stavů je řazeno od nejsilnějšího po nejslabší a proven a inputs_unchanged se nikdy nesloučíimplementováno, unit testy v src/evidence/mod.rs
jen guaranteed je držen u svých důkazůimplementováno, unit testy v src/evidence/mod.rs
sloučení jednoho testu nechává důkazy každého jiného testu nedotčené; soubor je seřazený a projde zpáteční cestouimplementováno, unit testy v src/evidence/ledger.rs
kniha neznámé verze nebo nečitelného tvaru se zamítne, ne reinterpretujeimplementováno, unit testy v src/evidence/ledger.rs
chybějící kniha a prázdná kniha jsou obě „nic nezaznamenáno“, nikoli chybaimplementováno, unit testy v src/evidence/ledger.rs
zpráva sady se stane prováděními s původem; pozdější částečný běh aktualizuje jen to, co spustilimplementováno, unit testy v src/evidence/record.rs
poškozená zpráva, běh bez commitu a výsledek pro test, který neexistuje, se každý odmítne nebo jmenujeimplementováno, unit testy v src/evidence/record.rs
deklarace dává přesně ty projekce, které si nárokuje; zapisuje jen recorder a ten není na sítiimplementováno, unit testy v src/capability/builtin/evidence.rs
celá cesta od začátku do konce ve fixture repozitáři — nic nezaznamenáno, původ, částečný běh, stale s uvedenou cestou, proven proti inputs_unchanged, failing a návratový kód, oba směry, odmítnutíbehaviorálně ověřeno (test/cases/124_evidence.sh)
kontrola plánovaná při každém pushi a cesty, které mohou změnit odpověďdeklarováno v .ai/repo/ci/gates.yaml; plánovač, který to čte, je behaviorálně ověřen (test/cases/94_ci_plan.sh)
CI zaznamenává vlastní běhy, opatřené razítkem běhu, do artefaktu evidence commitubehaviorálně ověřeno (test/cases/357_ci_records_evidence.sh); viz Zaznamenáno v CI
web zveřejňující tyto důkazy jako aktuální, zastaralé se svou vzdáleností, nebo nedostupné s důvodembehaviorálně ověřeno (test/cases/357_ci_records_evidence.sh); vykresleno na /evidence/
důkazy z CI commitnuté do sledované knihyodmítnuto — ADR 68: běh nemůže commitnout, co prokázal, aniž by přidal commit za ten, který prokázal
web vykresluje stav důkazu tvrzení vedle jeho statusuneimplementováno; stránka záruk stále ukazuje samotný deklarovaný stav
odolnost proti manipulaci, podepsaná kniha, historie běhů, zachycený výstupneimplementováno a odmítnuto — viz Co to není a ADR 40

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.