Pravidlo není vynucováno tím, že je zapsáno.
Pravidlo je vynucováno, když je deklarováno, implementováno, zapojeno, spouštěno, propaguje selhání, je testováno, blokuje CI a je propojeno s tvrzením. Cokoli méně je dokumentace. Tato vrstva existuje proto, aby ten rozdíl znala a řekla ho jedním příkazem.
Řetězec
flowchart TD principle["principle<br/><small>why the rule exists at all</small>"] doctrine["doctrine<br/><small>what must be true · share/standard/majordomus/</small>"] validator["validator<br/><small>whether it is true · mj_validate_#lt;name#gt; in lib/</small>"] point["enforcement point<br/><small>when the validator runs · the commands in enforced_by</small>"] propagation["propagation<br/><small>what a violation costs · the command's exit code</small>"] test["test<br/><small>proof of the behaviour · test/cases/</small>"] ci["CI<br/><small>proof it blocks integration · .github/workflows/validate.yml</small>"] claim["claim<br/><small>what may be promised · docs/CLAIMS.yaml</small>"] principle --> doctrine --> validator --> point --> propagation --> test --> ci --> claim
majordomus doctor prochází tento řetěz pro každou doktrínu a hlásí první článek, který se láme. Čte zdroj, ne vlastní popis registru o sobě, takže registr, který lže o tom, co vynucuje, selže tady, a ne v produkci.
Registr
Registr se odvozuje, nikdy nepíše: je to každé pravidlo efektivní množiny repozitáře, jehož blok x-majordomus jmenuje validátor, v pořadí vyřešených závislostí. Efektivní množinou je balíček, který nástroj dodává (share/standard/majordomus/), vendorovaný do repozitáře pod .ai/repo/rules/vendor/majordomus/, plus vlastní pravidla repozitáře pod .ai/repo/rules/project/. Repozitář nevymýšlí doktríny Majordoma, protože doktrína je výrok o tom, jak se nástroj chová, a každý uživatel nástroje má nárok na totéž chování; smí přidat vlastní pravidla a smí je vynucovat vlastním validátorem ve stejném formátu.
Každá doktrína, čtená ze svého objektu pravidla, nese:
| pole | význam |
|---|---|
id | id pravidla, majordomus.<name> pro základní linii; používá ho --rule, web a zprávy o odmítnutí |
title, description, statement | co znamená, po větě na každé |
class | blocking nebo advisory — viz níže |
depends_on | pravidla, na kterých stojí, a princip, kterému mezi nimi slouží |
x-majordomus.validator | přípona funkce mj_validate_<validator>, která o tom rozhoduje |
x-majordomus.category | kategorie zjištění, pod kterou se porušení hlásí |
x-majordomus.enforced_by | příkazy, které ji dispečují |
x-majordomus.exit_code | návratový kód, který porušení vyvolá (ze stávající smlouvy; žádná doktrína žádný nevymýšlí) |
x-majordomus.policy_key | pro doktríny dokončení, název, který repozitář používá v verification.finish_requires |
x-majordomus.claims | id tvrzení v docs/CLAIMS.yaml, která tato doktrína podporuje |
x-majordomus.tests | případy, které to dokazují |
Objekty pravidel
Pravidla jsou přenosné objekty pravidel: jeden soubor Markdown na pravidlo, YAML front matter na strojové straně, próza na lidské, pod sekcí pravidel repozitáře. Jsou čitelná bez nástroje a nástroj nečte nic jiného.
.ai/repo/rules/
├── README.md the format, for whoever writes a project rule
├── vendor/majordomus/ the pinned Majordomus baseline; do not edit, upgrade explicitly
│ ├── manifest.yaml every rule file, its identity and its content hash
│ └── rules/*.md
└── project/*.md rules this repository wroteFront matter
Identitou je id a version, nikdy název souboru. Obecná pole jsou ta, kterým rozumí každý čtenář formátu; blok x-majordomus je vazba nástroje a je přítomen jen u pravidla, které nástroj vynucuje.
---
id: majordomus.scope-integrity # namespaced by origin; project rules use another prefix
version: 1 # an exact integer
kind: rule
title: Scope integrity
description: One sentence.
statement: The normative sentence a worker follows.
status: active # active | deprecated
class: blocking # blocking | advisory — the same two classes, no third
depends_on: [majordomus.state-consistency@1] # exact id@version references, or []
tags: [scope, verification]
x-majordomus:
validator: scope # mj_validate_<validator>
category: scope # the finding category
enforced_by: [check, finish, watch]
policy_key: scope_respected # finish doctrines only
exit_code: 10
claims: [scope-enforcement] # ids in docs/CLAIMS.yaml
tests: [test/cases/04_start_check.sh]
---
Povolené klíče jsou uvedeny v share/allow/rule.txt; klíč mimo ten seznam je chyba, ne tichý přebytek. Obecný čtenář může x-majordomus ignorovat a pravidlu stále rozumí. Pravidlo bez bloku je normativní pro každého, kdo jej čte, a nevynucuje je nikdo, a majordomus rules list o něm říká not machine-enforced, místo aby naznačovalo opak. Třída stále říká, co porušení znamená.
Pravidlo, které je class: blocking a nemá blok, je proto slib, který tento nástroj sám nesplní, a většina blokujících pravidel tohoto repozitáře je v této pozici — změřte to pomocí majordomus doctrine status, který vypíše velikost registru vedle velikosti efektivní množiny a počet uvede na vlastním řádku. Počet je velký, předchází reportu a čísla je třeba číst opatrně: většina těch pravidel je vynucována, kontrolou pod scripts/ci/ nebo případem pod test/cases/ jmenovaným v jejich sekci # Verification. Co registr doktrín nevidí, není totéž co to, co nevynucuje nic, a povrch, který hlásí jedno, zatímco čtenář slyší druhé, je defekt, kterému project.a-verdict-states-its-subject existuje zabránit.
scripts/ci/rule-proof-check rozhoduje o rozdílu a je jediným čtenářem té populace: blokující pravidlo musí ve svém bloku x-majordomus pojmenovat validátor, který dispatcher volá, test, který ho dokazuje, nebo reviewed_because: říkající, proč to nic spustitelného nevyjádří. Pravidla, která nepojmenovávají nic, jsou ratchetem v .ai/repo/rule-proof-baseline.txt, aby se množina mohla zmenšit a nemohla růst. Odpovědí je zapojení vynucování, nikdy změna třídy pravidla — přesunout slib neznamená dodržet ho.
Druhý konec téže myšlenky je tvrzení, ne pravidlo, a má vlastní kontrolu: scripts/ci/claim-proof-check odmítne tvrzení označené status: guaranteed v docs/CLAIMS.yaml, jehož pojmenovaný test neobsahuje id tvrzení, takže se vazba čte z obou konců, ne jen od tvrzení. Obě kontroly jsou samostatní čtenáři samostatných populací a každá to při každém běhu říká. Byly jedním skriptem a pravidlová polovina přijímala jako důkaz jakýkoli blok x-majordomus — včetně reviewed_because:, který deklaruje, že pravidlo vynucuje člověk. Tyto výjimky ohlašovala jako sliby, které získaly důkaz. Druhý soupis vlastnosti, kterou vlastní jiná kontrola, nezůstane neškodným duplikátem; rozchází se a pak se oba verdikty neshodnou na věci, kterou toto úložiště existuje, aby tvrdilo.
Řešení
majordomus rules list řeší účinnou sadu jako graf závislostí, deterministicky: vendorovaný balíček v pořadí svého manifestu, pak projektová pravidla v pořadí názvů souborů, seřazená tak, aby každá závislost předcházela pravidlu, které na ní závisí. Každé z těchto zastaví řešení s exitem 10 a důvodem a nic se neaplikuje částečně:
- závislost, kterou neposkytuje žádné pravidlo, nebo jen zastaralé pravidlo,
- cyklus závislostí,
- jedno
id@versionnárokované dvěma soubory, - pravidlo projektu, jehož id je v jmenném prostoru
majordomus., - front matter, který chybí, neparsuje se, postrádá povinné pole nebo nese neznámý klíč,
- blok
x-majordomusbez validátoru, bez vynucujícího příkazu nebo bez testu.
Vendoring
Základna pod vendor/majordomus/ je kopií balíčku, který spustitelný soubor dodává v share/standard/majordomus/, zapsanou příkazem init a poté už jen příkazem majordomus rules vendor update. Manifest dodávaného balíčku zapisuje jeho správce příkazem scripts/rules-package write — spouští jej scripts/derive — a kontroluje scripts/rules-package check v sadě testů a v scripts/derive-check; žádný příkaz, který běží ve spravovaném repozitáři, do distribuce nezapisuje. Jeho source_revision je release, který strom produkuje, čtený z manifestu crate, takže štítek, který adoptující vidí v rules vendor status, odliší jeden balíček od druhého, a check odmítne razítko, které po sobě nechal bump. Pravidla balíčku jsou psána pro libovolného adoptujícího: incident, který naučil některé z nich, se vypráví jako incident repozitáře Majordomus, nikdy jako „tento repozitář“, což by ve vendorované kopii jmenovalo adoptujícího (test/cases/3022). Kopie repozitáře je pro tento repozitář autoritativní: novější spustitelný soubor hlásí novější základnu přes rules vendor status a rules vendor diff a nikdy ji neaplikuje. Manifest jmenuje každý soubor pravidel s jeho hashem, takže ruční úpravu pod vendor/ detekuje rules vendor status a rules vendor update ji odmítne, dokud --force. Aktualizace je atomická a nikdy se nedotkne rules/project/.
Skládání: aditivní, bez přepisování
Efektivní množina je každé aktivní vendorované pravidlo plus každé aktivní pravidlo projektu. Pravidlo projektu smí přidat omezení. Mechanismus přepsání neexistuje: nic nevypne ani neoslabí vendorované pravidlo a pravidlo projektu nesmí znovu použít vendorovanou identitu ani její jmenný prostor. Repozitář, který potřebuje vendorované pravidlo pryč, změní základní linii explicitně, otevřeně, přes rules vendor update, nebo nástroj nepoužívá.
Co je autoritativní
Objekty pravidel a nic jiného. Dispatcher v lib/doctrine.sh načte efektivní sadu repozitáře přes lib/rules.sh a každé pravidlo s blokem x-majordomus bere jako doktrínu; doctor, doctrine list, check --rule a web čtou stejnou sadu. Sada, která se nevyřeší — chybějící závislost, cyklus, dva soubory nárokující jednu identitu, poškozený soubor — zastaví příkaz, který ji potřeboval, s kódem 10 a důvodem, a nic se nevynucuje částečně. Řetěz zapojení — deklarováno, validátor existuje, příkazy, které jmenuje, ho spouštějí, blokující selhání se šíří, každý test, který jmenuje, existuje, CI spouští sadu — se znovu vytvoří nad objekty a zpětná kontrola (každá funkce mj_validate_* je deklarována efektivním pravidlem) zůstává. Ručně upravený vendorovaný soubor je selháním doctor přes majordomus.rule-package-integrity.
Dvě třídy a žádná třetí
- blokující — porušení zastaví příkaz nenulovým návratovým kódem.
- advisory — porušení se ohlásí a příkaz přesto uspěje.
Třída není popisek na diagramu. mj_doctrine_fail ji čte a podle ní směruje zjištění, takže změna advisory na blocking v registru mění, zda majordomus check skončí s 0 — a test/cases/17_doctrine_enforcement.sh převrací přesně tuto hodnotu a ověřuje, že se návratový kód posune. Které doktríny jsou dnes poradní, je odvozeno, ne zde napsáno: majordomus doctrine list vypisuje třídu každé z nich. Vše, co není poradní, blokuje.
watch klade týmž doktrínám jinou otázku — ne je to špatně, ale pohnulo se to — takže pod watch se porušení hlásí jako DRIFT a návratový kód je 11, včetně doporučujících doktrín: watch nikdy neblokuje práci, takže třída tam nemá o čem rozhodovat. Pravidlo, validátor i zpráva jsou stejné.
Ne každá doktrína je sledovatelná a registr říká která tím, že vynechá watch z enforced_by. Nevyřešená otázka je nejjasnější případ: je to zaznamenaný stav, zapsaný záměrně, a opak driftu. Soubor otázek, který už nelze naparsovat, je drift, a to je jiná doktrína.
Tři režimy vynucování
Pravidlo říká, jak je vynucováno, ve vlastním bloku x-majordomus a tvar toho bloku je režim — není slovo k deklaraci a není slovo, které by šlo napsat špatně:
| režim | názvy bloků | kdo to spouští | co repozitář umí ukázat |
|---|---|---|---|
| odesláno | validator, s category, exit_code, enforced_by a alespoň jedním testem | mj_doctrine_dispatch, u příkazů, které enforced_by jmenuje | validátor běžel a případy dokazují, že rozhoduje správně |
| řízeno kontrolou | tests, a žádný validátor | kontrola CI, nebo behaviorální sada | případy existují a kontrola je spouští |
| zrevidováno | reviewed_because a ani jedno z výše uvedených | člověk | nic spustitelného — a důvod proč |
Třetí existuje, protože některá pravidla skutečně nemají strojový výraz. Provenience toho, co bylo napsáno je jedno; zda si mechanismus zaslouží svou cenu je druhé. Dříve ležela v seznamu dluhů nazvaném rule-proof-baseline.txt vedle pravidel, na která se prostě nedostalo, a ty dvě skupiny byly k nerozeznání — což je horší selhání, protože seznam šlo číst jen jako „práce, která ještě zbývá“, a polovina z ní nikdy nezbude.
reviewed_because je celá deklarace. Vedle není záměrně žádný boolean: příznak lze nastavit, ale důvod nelze nastavit, aniž by se napsal. Výjimka je proto vždy věta, kterou někdo musel obhájit, a revizor čte větu, místo aby počítal položky v seznamu.
Dvě vlastnosti brání tomu, aby se z toho stala cesta ven:
- spustitelný důkaz vždy vyhrává. Pravidlo, které pojmenovává test, je kontrolováno bez ohledu na to, zda je důvod stále v jeho front matteru, takže pravidlo, které získá případ, přestane být vynucováno review, aniž by si kdokoli musel pamatovat cokoli smazat.
- počítá se zvlášť, všude.
rule-proof-checkhlásí každý z nich jakoNOTEpři každém běhu a vypisuje počet vedle naměřeného součtu; typovaná zpráva je počítá vreview_only, nikdy vpassinga nikdy vnamed_proof. Když toto číslo roste, správa slábne, a žádné shrnutí ho nezapočítá do součtu, který by to skryl.
Graf důkazů
majordomus doctrine odpovídá na otázku splňuje repozitář toto pravidlo právě teď. To je otázka o stromu. Existuje druhá otázka — je toto pravidlo ve stavu, v němž by vůbec mohlo být splněno — která se týká pravidla, a do vzniku grafu důkazů ji nikdo nekladl. crate::rules je jediné typované čtení, které ji klade, a majordomus-cli rules ho promítá na každý povrch.
Pro každé pravidlo spojuje deklaraci se stromem a s ledgerem zaznamenaných běhů (EVIDENCE.md) a hlásí jeden stav:
| state | co to znamená |
|---|---|
proven | každý jmenovaný artefakt je ve stromě a každý jmenovaný případ má úspěšný běh, od kterého se nic nezměnilo |
inputs unchanged | totéž, až na to, že alespoň jeden běh je jen neinvalidován — nic z toho, co jmenuje, se nezměnilo, což je absence známé invalidace, nikoli důkaz proti tomuto commitu |
stale | úspěšný běh existuje a soubor, kterého se týká, se od něj změnil |
gated | spustitelná kontrola porušení odmítne a kontrola v CI ji spouští; tento repozitář nezaznamenává pro kontrolu žádný verdikt, takže lze ukázat mechanismus, a ne výsledek |
failing | poslední zaznamenaný běh neprošel |
not run | případ vlastní runner a nikdy nebyl zaznamenán jeho běh |
reviewed | pravidlo deklaruje i s důvodem, že jej nic spustitelného nevyjádří |
unrunnable | pravidlo jmenuje cestu, kterou nespouští žádný runner, takže ji nic nikdy nemůže zaznamenat |
dangling | pravidlo uvádí cestu, která ve stromě není — čte se jako prokázané, a není |
unproven | pravidlo nejmenuje ani validátor, ani test, ani důvod |
Stav pravidla je nejslabší z jeho částí, s jedinou přednostní výjimkou: selhávající test učiní pravidlo failing, ať říkají ostatní testy cokoli, protože řazení staví failing nad not run a selhání se nikdy nesmí schovat za absenci. Jinak je pravidlo nejslabší z částí, které mohou nést důkaz, a cesta, kterou nespouští žádný runner ani kontrola, se počítá jen tehdy, je-li vše, co pravidlo má. Jeden visící případ učiní pravidlo visícím, kolik jeho ostatních případů prochází. Dva stavy, které nic jiného v tomto repozitáři nevyjádří, jsou ty poslední dva, a jsou důvodem, proč graf existuje. dangling je jednosměrný vztah, který tento repozitář pořád znovu objevuje — důkaz ověřený podle jména, s opačným směrem bez vlastníka, takže případ přejmenovaný v lednu nechá pravidlo v červnu číst jako vynucené. unproven je polovina, kterou žádná jiná kontrola nehledá: nic jiného neví, že blokující pravidlo má vůbec něco jmenovat.
Zjištění je pravidlo, jehož deklarovanou třídu stav nepodporuje. dangling je zjištěním v každé třídě, protože se čte jako důkaz a není jím. U blokujícího pravidla jsou zjištěními také unproven, failing a unrunnable: každé činí tvrzení „kontrola odmítne práci, která toto porušuje“ nepravdivým. gated, reviewed a not run zjištěními nejsou — jsou to slabší stavy, řečené nahlas a započítané, což je něco jiného než vada.
Protože nejsou zjištění, „žádná zjištění“ neznamená „dokázáno“ a hlášení nikdy nedovolí číst jedno jako druhé. satisfied je přesně „nejsou žádná zjištění“. verdict hlášení, vytištěné jako první v textu a nesené na vrcholu JSON, je to, o čem lze říct, že to korpus dokazuje: failing, když je jakékoli zjištění; proven jen když je každé blokující pravidlo proven; jinak unproven — blokující pravidlo nikdy nespuštěné, kontrolované, posouzené či zastaralé, blokující pravidlo, jehož vstupy se jen nezměnily, nebo žádné blokující pravidlo. Korpus bez zaznamenaného běhu za jakýmkoli blokujícím pravidlem je satisfied a unproven, což je o něm pravda.
Čitelné jsou oba směry. rules show odpovídá, co dokazuje pravidlo; rules proves odpovídá, co dokazuje test, a pojmenovává pravidla, která by po jeho smazání zůstala zcela bez důkazu. To je otázka, kterou nešlo položit, dokud relace šla jen jedním směrem, a je to ta, kterou je třeba položit před přejmenováním případu.
Dispečer
Žádný příkaz nejmenuje validátor. Každý volá mj_doctrine_dispatch <command>, který prochází registr a spouští každou doktrínu, jejíž enforced_by obsahuje daný příkaz. Doktrína přidaná do registru se vynucuje od té chvíle, aniž by se změnil jediný příkaz.
Tři věci jsou chyby konfigurace, ne výsledky pravidel, a každá to říká vlastními slovy:
- doktrína, jejíž validátorová funkce neexistuje,
- doktrína, jejíž třída není ani
blocking, aniadvisory, - validátor, který skončí nenulově (validátor hlásí porušení přes
mj_doctrine_fail, takže nenulový návrat znamená, že se rozbil sám validátor).
Všechny tři selhávají uzavřeně. Žádný z nich se nikdy nehlásí jako čistý běh.
Co doctor ověřuje
Pro každou doktrínu:
- validační funkce je definována někde v
lib/; - každý příkaz v
enforced_byexistuje a volámj_doctrine_dispatch; - příkazy blokující doktríny mohou z neúspěšného nálezu udělat nenulový exit kód;
- testovací soubor, který jmenuje, existuje;
- každé tvrzení, které jmenuje, je v
docs/CLAIMS.yaml.
A v opačném směru: každá funkce mj_validate_* v lib/ je deklarována nějakou doktrínou. Osiřelý validátor je vynucování běžící pod žádným pravidlem, tak kontrola tiše přestane být řízená, a proto selže.
A pro pipeline: validate.yml spouští test/run.sh, nepohlcuje jeho exit kód a test/run.sh globuje test/cases/ místo vypisování případů jménem — runner, který případy vypisuje, je runner, ze kterého nový případ může chybět.
test/cases/18_doctrine_wiring.sh v jednorázové kopii nástroje přeruší každý z těchto devíti článků a selže, pokud doctor nezčervená. Verifikátor, který přežije porušené zapojení, nic nedokazuje.
Dokončení je balík doktríny
majordomus finish nenese vlastní seznam řádků kontraktu. Kontrakt je množina doktrín, jejichž enforced_by jmenuje finish, a verification.finish_requires repozitáře vybírá, které z nich aplikuje — přes policy_key, takže se oba seznamy nemohou tiše rozejít. Požadavek v politice, který žádná doktrína nedefinuje, se hlásí a odmítá; neignoruje se tiše.
Odmítnutí pojmenovává odpovědné doktríny:
finish: refused, 1 unmet
blocking doctrines:
- majordomus.blocker-resolution
Jeden řádek kontraktu se repozitáře netýká vůbec. publication_current se ptá, zda to, co se veřejnosti servíruje, je stále projekcí trunku — otázka, pro kterou nelze vybrat žádnou kontrolu v modelu CI, protože na ni žádná změna žádného souboru neodpoví. Doktrína majordomus.publication-currency spouští kontroly, které model CI označuje at-finish, živě, v okamžiku tvrzení, a odmítne completed, dokud jedna z nich říká, že se publikace dluží. Spouští se a nikdy se nezaznamenává: důkaz, jehož předmět se může změnit, zatímco strom stojí, nemá hash, proti kterému by vypršel.
Doktríny znalostí pokrývají oba programy. majordomus.knowledge-observed a majordomus.knowledge-integrity rozhodují validátory v lib/knowledge.sh, vedle shellového zapisovatele, který záznamy odvozuje, a knowledge_base.status rustového čtenáře činí polovinu čerstvosti téhož posouzení zastaveného zapisovatele ze stejného ledgeru a stejných čísel politiky; polovina zapojení — že zdroj životního cyklu volá odvozovač — je jen věcí shellového validátoru, protože jen shellový nástroj umí číst svůj vlastní zdroj. KNOWLEDGE.md má tři pravidla a co každé nachází.
Čtení z příkazové řádky
majordomus doctrine status # derived counts: declared, blocking, advisory, unwired
majordomus doctrine list # id, class, validator, the commands that enforce it
majordomus doctrine show <id> # the full record, including claims and test
majordomus check --rule <id> # run one doctrine
majordomus doctor # verify the whole chain
Počty se odvozují při každém volání. Nikde, ani zde, nejsou zapsány.
Co bylo odmítnuto
Návrhovým vstupem pro tuto vrstvu byl vynucovací systém velké platformy. Většina z něj do přenosného shellového nástroje nepatří a vynechané části jsou stejně záměrné jako ty zachované.
- Žebříček závažnosti. Blokující nebo poradní. Pravidlo, které není ani jedno, je poznámka v dokumentu, nikoli doktrína.
- Základny a ratchety. Ratchet nechá starší porušení zůstat a blokuje nová. Je to správná odpověď pro repozitář s nahromaděným dluhem a špatná pro nástroj, který žádný nemá: soubor základny je místo, kde lze číslo tiše zvýšit. Majordomus vynucuje absolutně. Pokud repozitář, který jej adoptuje, nemůže doktrínu splnit, poctivý krok je to říct, ne zakódovat mezeru jako povolený strop.
- Mechanismus přepsání. Žádný
MAJORDOMUS_BYPASS, žádný trailer výjimky, žádná kniha amnestií. Přepsání, které existuje, se používá, a pak se prací stane auditní stopa přepsání.finishuž má typované výsledky —partial,blocked,failed,no_match— které říkají, co se skutečně stalo, místo aby předstíraly, že smlouva byla splněna. - Auditní kniha událostí vynucování. Stávající
state/ledger.jsonluž zaznamenává smlouvu dokončení řádek po řádku, pod stropem uchování. Druhá kniha porušení by rostla, aniž by zodpověděla otázku, na kterou ta první nemůže. - Jazyk výrazů politiky.
enforced_byje seznam jmen příkazů porovnávaných přesně. Cokoli, k čemu je třeba gramatika, je validátor, ne pravidlo. - Exit kódy podle doktríny. Zjištění se mapují na stávající kontrakt — 10 kontrakt nesplněn, 11 drift, 12 chybí artefakt, 13 vnitřní, 15 odmítnuto. Doktrína si nesmí vymýšlet číslo.
- Hooky všude. Vynucování běží přes příkazy životního cyklu. Repozitář se rozhodne tyto příkazy zapojit do git hooků a
doctorověřuje zapojení, které deklaroval; nástroj hooky za zády repozitáře neinstaluje.
Selhání, kterému má toto zabránit
rule documented
+ script exists
+ test exists
+ nothing invokes it
= fake enforcement
Každý z těch čtyř je přítomen ve stavu selhání, a proto jejich počítání není kontrola. doctor místo toho stopuje volání.