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.

Specifikace CLI

každý příkaz: chování, čtení, zápisy, smlouva o návratových kódech, cílový výstup

Vykresleno z docs/CLI.md — stejný Markdown, jaký ukazuje GitHub.

Chování tak, jak je implementováno a testováno v test/cases/. Kde se implementace a tento dokument rozcházejí, je špatně dokument a mění se ve stejném commitu jako oprava.

Jeden spustitelný soubor, bin/majordomus, přenosný shell (minimem je bash 3.2 a uživatelské prostředí BSD). Podpříkazy se předávají načítaným modulům v lib/. Každý podpříkaz přijímá --help a --repo <path> (výchozí: kořen gitu aktuálního adresáře).

Kontrakt návratových kódů

Návratové kódy jsou součástí rozhraní. Volající, včetně git hooku, je musí propagovat. Hook, který obdrží nenulový kód a pokračuje, porušuje smlouvu a doctor prohledává skripty hooků na || true a || exit 0 kolem vyvolání Majordomus.

KódNázevVýznam
0OKpříkaz uspěl; u kontrol nebylo nic nalezeno
2USAGEšpatné argumenty, nebo mimo repozitář git
10CONTRACT_UNMETselhal deterministický kontrakt: kontrakt dokončení, validace politiky, zapojení
11DRIFT_FOUNDwatch našlo alespoň jeden drift; informativní, hook podle něj nikdy neblokuje
12MISSING_ARTIFACTchybí požadovaný soubor nebo nástroj
13INTERNAL_ERRORselhal samotný Majordomus; nikdy se tiše nepovažuje za úspěch
15REFUSEDpříkaz odmítl přepsat nebo byl odmítnut override

Neexistuje kód „varuj a pokračuj“. Při nejednoznačnosti selhat uzavřeně: pokud je nejasné, zda kontrola prošla, selhala.

Konvence výstupu

  • Lidský výstup na stdout, jedno zjištění na řádek, strojově prohledatelný: <LEVEL> <category> <subject> — <message> [reproduce: <command>]
  • Úrovně: OK, INFO, WARN, FAIL, DRIFT, REFUSE. Na návratový kód mají vliv jen FAIL a DRIFT. WARN je rada k rozdělané práci a nikdy neblokuje.
  • --json u libovolného příkazu jen pro čtení vypíše na stdout jeden objekt JSON na nález.
  • Na stderr se nezapisuje nic kromě chyb použití a vnitřních chyb.
  • Zjištění bez příkazu pro reprodukci je chyba v Majordomus.
  • MAJORDOMUS_CAPABILITY_CACHE=off spustí každou schopnost bez cache exekutoru: každé volání spustí svůj handler a nic se neuchovává. Odpovědi jsou s ním i bez něj stejné (project.cache-is-invisible); existuje proto, aby se pochybnost rozhodla dotazem bez cache. Politika schopnosti říká, pod čím se její odpověď uchovává: otisk registru, čtení repozitáře kontextem (generation), nebo i stav checkoutu (checkout: HEAD, staging index, refs a každá změněná či nesledovaná cesta). Přepínač také způsobí, že čtení zdroje stránkou spustí svůj handler a počká na něj.
  • MAJORDOMUS_READING_PATIENCE_MS určuje, jak dlouho stránka po změně repozitáře čeká na zdroj, než ukáže odečet, který má, označený jako zastaralý (výchozí 50; viz MAJORDOMUS_UI.md). wait způsobí, že každé čtení čeká na aktuální odečet, což potřebuje test, který změní repozitář a pak čte stránku, aby byl přesný.

majordomus init

Vytvořit vrstvu AI repozitáře, .ai/, z kostry nástroje. Nic neinstaluje: nástroj zůstává tam, odkud byl spuštěn, nedotkne se žádného hooku ani souboru shellu a .majordomus/ se nikdy nevytvoří.

Čte: skeleton pod share/skeleton/ a standardní balíček pravidel pod share/standard/majordomus/. Zapisuje: .ai/README.md (protokol, čitelný bez nástroje) a .ai/manifest.yaml (registr sekcí, ai-repository/v1); pod .ai/repo/ sledovanou polovinu: jeho README.md, policy.yaml, profiles/, prompts/, rules/ s jeho README.md, vendorovanou baseline rules/vendor/majordomus/ (manifest a soubory pravidel, bajt po bajtu balíček, který nástroj dodává) a prázdné rules/project/, knowledge/sources.yaml vedle prázdného knowledge/curated/, workflows/, skills/, adrs/, project/; pod .ai/local/ vlastní polovinu checkoutu: state/ osazené decisions.md a open-questions.md a prázdné state/handovers/ a state/checkpoints/, plus cache/, prompts/ a session-contexts/; a jeden řádek .ai/local/ v .gitignore, přidaný jednou. Vše pod .ai/repo/ od té chvíle patří repozitáři; novější nástroj to nepřepisuje.

Odmítne (15), když .ai/ již existuje, pokud není --extend, který přidá každý soubor, jenž kostra dodává a repozitáři chybí, a nic nepřepíše; (15), když .ai/ existuje bez manifestu, a pojmenuje volbu mezi odsunutím stranou a --extend; a (15), když data projektu stále leží pod .majordomus/, v rozvržení před .ai, a pojmenuje majordomus migrate.

Neinstaluje git hooky. Vypíše dva řádky, které hook potřebuje, a příkaz majordomus doctor, který ověří, že byly přidány.

$ majordomus init
created
  .ai/README.md
  .ai/manifest.yaml
  .ai/repo/README.md
  .ai/repo/policy.yaml
  .ai/repo/profiles/
  .ai/repo/prompts/
  .ai/repo/rules/README.md
  .ai/repo/rules/vendor/majordomus/
  .ai/repo/knowledge/
  .ai/repo/workflows/
  .ai/repo/skills/
  .ai/repo/adrs/
  .gitignore:.ai/local/
local state: .ai/local/state/ (ignored by git; this checkout's own)
next: majordomus update      # generate the provider instruction files named in the policy
next: majordomus doctor      # verify nothing is declared that is not wired

$ majordomus init
majordomus: .ai/ already exists in … (use --extend to add what is missing; nothing is overwritten)
$ echo $?
15

majordomus migrate

Přesunout projektová data repozitáře z uspořádání před .ai pod .majordomus/ do přenosné AI vrstvy pod .ai/, jednou a výslovně. Žádný běžný příkaz nemigruje: check, doctor, start a ostatní zastaré uspořádání odmítnou (12) a pojmenují tento příkaz.

Značky: starý je .ai/repo/policy.yaml; nový je .ai/manifest.yaml; .majordomus/bin/majordomus je instalace nástroje a nikdy data projektu.

Čte: každý soubor pod .majordomus/, manifest skeletonu (odtud jsou cíle), šablony nástroje (aby se odlišila nezměněná šablona od upravené). Zapisuje: .ai/README.md, .ai/manifest.yaml; kanonické soubory přesunuté do .ai/repo/ (git mv tam, kde jsou sledovány, aby historie následovala); .ai/local/state/ přesunuté do .ai/local/state/ a vyjmuté z indexu; zbytek vrstvy osazený ze skeletonu bez přepsání čehokoli, co se přesunulo; jeden řádek .ai/local/ v .gitignore; bajt po bajtu kopii stavu pod tmp/majordomus-migrate-backup/<utc>/state/ a starého providers/ pod .../providers/, ověřenou a vypsanou, než se kterékoli z nich dotkne; řádek ledgeru layout.migrated; poté update --force vykreslí projekce z tenkých bootstrapů nástroje a doctor posoudí výsledek, každý exit je hlášen na vlastním řádku.

Behaviour:

  • --dry-run vypíše celý plán, jeden řádek na soubor s jeho akcí a cílem, a nic nezapisuje. Stejná tabulka řídí skutečný běh.
  • templates/*.md shodné s vlastními v nástroji se zahodí; upravené se přesune do .ai/repo/templates/. generated/ se zahazuje: každá projekce teď nese vlastní razítko.
  • providers/ — tělo poskytovatele a monolitické šablony, které si ho vyžádaly — se do .ai/ nepřenáší. Tělo už neexistuje nikde a update žádné nevykresluje, takže by stará šablona zanechala v generovaném souboru doslovný token. Adresář se zkopíruje do zálohy, odstraní z indexu a ohlásí jedním řádkem: bootstrapy jsou nyní tenké adaptéry nástroje, přepsání pro repozitář patří pod .ai/repo/providers/<provider>.tmpl v novém formátu a pravidla těla patří pod .ai/repo/rules/project/ jako objekty pravidel (DOCTRINE.md popisuje formát).
  • Soubor v .majordomus/, který tato verze nezná, se nikdy nesmaže. Ohlásí se a .majordomus/ se odstraní, jen když je prázdný.
  • Odmítne (15) .majordomus/, který obsahuje policy.yaml i bin/majordomus, a pojmenuje bezpečný ruční krok; odmítne (15), když cíl pod .ai/ již existuje, a vypíše každý takový. V obou případech se nic nezapíše.
  • Starší politika, která se neparsuje nebo není verze 1, se odmítne (10) dřív, než se cokoli přesune: nejdřív ji oprav na místě.
  • Idempotentní: v repozitáři, který už má rozvržení .ai, to řekne a skončí s 0. Pokud není přítomno žádné rozvržení, skončí s 12 a jmenuje init.
  • Stav přestane být sledován: jeho trvanlivost je checkout plus záloha, ne branch. Commitněte sledovanou polovinu po kontrole git status.
$ majordomus migrate --dry-run
migrate: .majordomus/ (pre-.ai layout) -> .ai/ (ai-repository/v1)
  move  .ai/repo/policy.yaml -> .ai/repo/policy.yaml
  move  .ai/repo/profiles/debugging.yaml -> .ai/repo/profiles/debugging.yaml
  state .ai/local/state/current.yaml -> .ai/local/state/current.yaml
  drop  .majordomus/templates/handover.md    (identical to the tool's template)
  ...
dry run: nothing written

majordomus recover

Vrátit úložiště záznamů tohoto checkoutu do stavu, který popisuje kontrakt: zavřít epizody, které nikdo neukončil, sloučit duplicitní záznamy jedné epizody do jednoho a roztřídit zbloudilé soubory, které se v úložištích hromadí. Opakovaná, idempotentní údržba — na rozdíl od migrate, což je jednorázový přesun z rozvržení před .ai, který odmítne běžet dvakrát.

Subjekty: status (výchozí, pouze čtení), episodes, records, orphans, all.

Čte: .ai/local/state/sessions-open/, .ai/local/state/ledger.jsonl, úložiště uzavřených záznamů a úložiště checkpointů a předávek. Zapisuje: uzavřený záznam pro každou obnovenou epizodu pod .ai/repo/sessions/, sloučený záznam tam, kde existovaly duplicity, řádek knihy session.recovered pro každou akci a odstraňuje otevřené záznamy a zatoulané dočasné soubory, na kterých zasáhl.

Behaviour:

  • --check vypíše plán a důkazy za ním a nic nezapíše. Totéž měření řídí skutečný běh, takže plán je akce.
  • Zbloudilý soubor se posuzuje podle stáří dřív než podle obsahu: dočasný soubor publikace starý vteřiny patří session close, které právě běží, a .mj-stage.XXXXXX staré minuty scripts/derive, které právě běží. Obojí se hlásí jako live a ani jedno se nedotkne; soubor, jehož stáří nelze přečíst, se přeskočí a započítá.
  • Zastaralý staging adresář se hlásí a nikdy neodstraňuje. Tento příkaz nemaže žádný strom adresářů — SECURITY.md uvádí „žádné rekurzivní mazání“ — takže .mj-stage.XXXXXX za prahem se jmenuje, počítá se jako přeskočený a nechá se člověku k odstranění.
  • Epizoda je uvízlá, když je její poslední známka života — pozdější z jejího vlastního started_at a nejnovějšího řádku knihy, který označila — starší než session.stranded_after. --older-than <duration> pro jeden běh přepíše politiku.
  • Mezi živou epizodou a úklidem stojí dvě pojistky a ani jedna není zálohou té druhé. Epizoda, na kterou se tento proces překládá, je vyloučena dříve, než se spustí jakýkoli predikát; a kandidát, jehož důkaz nelze přečíst jako časové razítko, se přeskočí a započítá, nikdy se nepovažuje za nejstarší v množině (project.destructive-sweeps-fail-closed). Epizoda patřící jinému worktree se ohlásí a tady se nikdy nezavře.
  • Obnovený záznam nese to, co dokazují otevřený záznam a vlastní řádky knihy epizody. commits a changed_files jsou explicitní prázdný seznam a head a working_tree chybí: popisují uzavření, které se nikdy nestalo, a pracovní strom v okamžiku obnovy patří tomu, kdo právě pracuje. outcome je interrupted, čím také bylo.
  • Duplicitní záznamy se sloučí do nejstaršího podle created_at s sjednocením seznamů, které ostatní dokládají, a názvy nahrazených souborů se zapíší do těla přeživšího. Záznamy, které se neshodují v identitě nebo časech epizody, se ohlásí a nic se neslučuje.
  • Nic se nemaže jen proto, že to nebylo rozpoznáno. Dočasný soubor publikace po prahu, který drží jedinou kopii záznamu, se publikuje; ten, jehož epizoda je už publikována, nebo prázdný, se odstraní; ten, který drží obsah, jejž tato verze neumí klasifikovat, zůstane přesně tam, kde je, a započítá se. Adresář, který někdo vložil do úložiště kontrolních bodů, se pojmenuje, změří a ponechá.
  • Idempotentní. Dvojí spuštění nad jedním předmětem provede jeho akce jednou a spuštění po pádu uprostřed běhu provede ty, které se nestaly: záznam se zapíše dřív, než se odstraní otevřený soubor, takže přerušení mezi nimi nic nestojí.
$ majordomus recover episodes --check
episodes — open episodes in .ai/local/state/sessions-open
  s-20260910194205-72ea  key=187c9e1e-…  last seen 2026-09-10T19:42:05Z (ledger, 13h ago)
    stranded: 13h ago, over the 12h threshold; close with outcome interrupted
  s-20260911063532-b41b  key=c93d195f-…  last seen 2026-09-11T08:31:02Z (ledger, 0m ago)
    live: this process is inside it; never a candidate

check: 1 action(s) would be taken and nothing was written (run: majordomus recover all)

majordomus doctor

Je řídicí dozorová vrstva zde skutečná? Pouze čtení. Záměrně blokující: určeno ke spouštění z pre-commit hooku a z CI.

Kontroluje pouze governance — politiku, profily, zapojení každého deklarovaného vynucování, projekce, záznamy a jejich retenci. Nečte nic ze serveru checkoutu, jeho lease ani z meshe, takže čistý report nic neříká o tom, zda je server odpovídající za tento checkout aktuální (I2707). Runtime se čte pomocí:

OtázkaKde se čte
Je server odpovídající za tento checkout aktuální, nebo zastaralý?majordomus serve status
Je mesh tady zdravý: identita, důvěra, spojení, runtime, žurnál?majordomus mesh doctor
Co o sobě hlásí server, včetně mesh?GET /api/v1/health (health.report)
Umí tento proces odpovědět: lease drží, spustitelný soubor je aktuální?GET /api/v1/ready (health.ready)

majordomus doctor --help vypíše totéž před svým použitím.

Kontroly v tomto pořadí:

  1. policy.yaml se naparsuje; version podporováno; žádné neznámé klíče na žádné úrovni.

  2. Každý profiles/*.yaml se parsuje; každý profil, na který politika odkazuje, existuje; žádné neznámé klíče.

  3. Zapojení vynucování. Pro každou položku v policy.enforcement: path se vyřeší (na PATH, relativně k repozitáři, absolutně nebo jako cesta ke spustitelnému souboru přímo na řádku hooku) a artefakt pojmenovaný wired_by existuje, je spustitelný, vyvolává majordomus <first arg> a nepohlcuje jeho kód ukončení přes || true nebo || exit 0. Když je hook dispečer, hledá se vyvolání v souboru hooku a v každém souboru jeho adresáře <hook>.d/; nález pojmenuje ten soubor, který ho skutečně nese. Subhook, který vyvolání nese, ale není spustitelný, se hlásí jako nezapojený, protože ho dispečer přeskočí. wired_by: manual se hlásí jako neověřeno, nikdy jako zapojeno. Deklarováno, ale nezapojeno je 10.

  4. Každý projections[].target existuje, jeho poskytovatel má šablonu a jeho položka v obsahu odpovídá razítku, které nese. Chybějící nebo neoznačený → 12; neshoda → 10 (ručně upraveno). Pro mode: region se hash bere jen z oblasti; chybějící nebo poškozené značky se tak hlásí.

  5. Projekce označená always_loaded: true je v mezích context.always_loaded_budget_lines.

  6. Každá cesta relativní k repozitáři, na kterou se odkazuje z vždy načítané projekce, se vyřeší.

  7. Žádné napevno zapsané počty v projekci, která se načítá vždy (posloupnost číslic vedle slov jako agents, files, apps, commands, skills, rules).

    Kontroly 5 až 7 posuzují pouze generovaný obsah. U projekce regionu je to region a nikdy hostitelský dokument, aby každé selhání, které doctor hlásí, šlo opravit úpravou politiky.

  8. Limity retence nepřekročeny na state/ledger.jsonl a state/handovers/.

  9. Sondy prostředí: verze bashe, git, jq a shellcheck, pokud je přítomen. Hlásí se jako INFO. Nic v doctor nepotřebuje nástroj nad bash, git a příkaz pro kontrolní součet.

$ majordomus doctor
OK   policy      .ai/repo/policy.yaml — parsed, version 1
OK   profiles    4 files — parsed
FAIL wiring      finish-contract — bin/majordomus is not invoked by .git/hooks/pre-push  [reproduce: grep -n 'majordomus finish' .git/hooks/pre-push]
OK   projection  CLAUDE.md — content matches its stamp
FAIL budget      CLAUDE.md — 212 lines, budget 150  [reproduce: wc -l CLAUDE.md]
OK   links       3 projections — all references resolve
OK   retention   ledger 412 lines, cap 5000
INFO env         bash 3.2.57, git 2.45, yq 4.44, jq 1.7
doctor: 2 failures
$ echo $?
10

majordomus start <task>

Zahájit úlohu s rozsahem.

Argumenty: <task> jeden řádek. --scope <path>[,<path>...] povinné. --profile <name> výchozí z policy.profiles.default. --owner <string> volný text, výchozí $USER. --issue <id>, --intent <id> nebo --exempt <class> --because "<reason>" pojmenovávají, čemu úloha slouží.

Čte: politiku, profil, stav gitu a intents.binding přes sestavený spustitelný soubor, když se žádá o vazbu. Zapisuje: state/current.yaml a jeden řádek task.started do state/ledger.jsonl.

Behaviour:

  • Odmítne (15), pokud state/current.yaml existuje a jeho výsledek není koncový. Jedna aktivní úloha na checkout: stávající úlohu předejte (handover --close) nebo ji nejdřív dokončete. Žádný přepínač aktivní úlohu nezahodí.
  • Normalizuje každou cestu rozsahu: odstraní koncové /, kanonizuje, odmítne cesty mimo repozitář. Zaznamená normalizovanou podobu.
  • Čte každý další worktree z git worktree list a tam, kde má aktivní úlohu, hlásí každý rozsah, který tento rozsah obsahuje nebo je jím obsažen. Hlásí se, neblokuje: je to koordinační fakt pro člověka, ne pravidlo. Git je registr; žádný pomocný soubor neexistuje.
  • Zaznamenává repository_id, branch, head, working_tree z gitu. Nikdy z argumentů.
  • Ptá se, k čemu je úloha navázána (ADR 0111), když je zadáno --issue, --intent nebo --exempt, a při každém startu, když je intent.binding z politiky advisory nebo required. Odmítnutá odpověď na otázku, kterou pracovník položil, nebo jakákoli odmítnutá či nečitelná odpověď pod required odmítne (15) a nic nezapíše, přičemž pojmenuje každou příčinu a tři cesty, jak dál. Pod advisory je odmítnutí varováním. Záznam drží, co bylo pojmenováno, postavení a dvě připnutí (plan_revision, evidence_standing); nic ze samotného záměru se neukládá. Plánování má postavení a příčiny.
$ majordomus start "fix OAuth callback" --scope lib/auth --profile debugging
started t-20260903-193012-a4f1  profile=debugging  scope=lib/auth
INFO overlap  ../wt-alice-oauth-refresh claims lib/auth/oauth — contained by your scope  [reproduce: majordomus check --overlap]
next: worker reads AGENTS.md; checkpoint every 15m; majordomus check

majordomus check

Je aktuální úloha konzistentní s politikou, rozsahem a stavem? Pouze čtení.

Checks:

  • state/current.yaml existuje a dá se parsovat; jinak 12.
  • Označení git divergence záznamu: exact, advanced, diverged, different_context. diverged a different_context jsou zjištění.
  • Dotčené soubory (git status --porcelain plus git diff --name-only <base>..HEAD) jsou v nárokovaném rozsahu. Cokoli mimo → nález.
  • Stáří checkpointu vůči checkpoint_interval profilu (WARN, nikdy FAIL).
  • Soubory pod .ai/ a cíle projekcí jsou vždy v rozsahu.
  • --checkpoint aktualizuje checkpoint_at a připojí task.checkpoint do ledgeru: jediný zdokumentovaný zápis v jinak čtecím příkazu.
  • state/open-questions.md nemá pro tuto úlohu žádnou nevyřešenou položku.
  • --explain vypíše efektivní sloučenou politiku a profil pro tuto úlohu a skončí s 0.
  • --overlap vypíše uzavření nároků vůči ostatním worktree.

Skončí s 0, když nejsou žádné nálezy FAIL, s 10 při jakémkoli nálezu, s 12 bez aktivní úlohy. Určeno pracovníkovi ke spuštění před prohlášením o dokončení a člověku kdykoli.

$ majordomus check
OK   state      t-20260903-193012-a4f1 — exact (head 3f2a9c1)
FAIL scope      config/secrets.example — outside claimed scope (lib/auth)  [reproduce: git status --porcelain; git diff --name-only 3f2a9c1 HEAD]
OK   checkpoint 7m ago, interval 15m
OK   blockers   none open
check: 4 finding(s), 1 failing

majordomus watch

Co se odchýlilo? Pouze čtení. Nikdy se nepoužívá k blokování; skončí kódem 11, když existují zjištění, aby skripty rozlišily „odchylku“ od „zdraví“ a nespletly si ji s selháním kontraktu.

DriftJak zjištěno
policyhash politiky, který uvádí razítko projekce, není politika na disku
projekceobsah projekce se liší od hashe, který uvádí její vlastní razítko
statevýsledek current.yaml odporuje účetní knize, nebo je štítek diverged
scopedotčené soubory mimo claim (stejná kontrola jako check)
handoverúloha je handed_over, ale žádná předávka ji nejmenuje, nebo té předávce chybí povinná sekce
ověřenícurrent.yaml má koncový výsledek bez záznamu task.finished v ledgeru
kontrolní bodcurrent.yaml starší než interval checkpointů profilu
uchováváníledger.jsonl nebo handovers/ nad limitem
$ majordomus watch
DRIFT policy      .ai/repo/policy.yaml modified after last update  [reproduce: majordomus update --dry-run]
DRIFT projection  AGENTS.md — content differs from its stamp (hand-edited?)  [reproduce: majordomus update --diff AGENTS.md]
DRIFT checkpoint  t-20260903-193012-a4f1 — last checkpoint 48m ago, interval 15m
watch: 3 findings

majordomus update

Znovu vygenerovat projekce poskytovatelů z politiky. Deterministicky: stejná politika, stejný výstup, bajt po bajtu.

Čte: politiku, profily, providers/*.tmpl. Zapisuje: každý projections[].target, jeden řádek ledgeru projections.updated. Nic jiného: každý cíl nese vlastní provenance.

Behaviour:

  • --dry-run vypíše, co by se změnilo; --diff <target> ukáže diff pro jednu. U projekce regionu je diff regionu, ne hostitelského dokumentu.
  • Odmítne (15) přepsat obsah, jehož aktuální hash neodpovídá ani razítku, které nese, ani novému výstupu, pokud není --force. Cíl bez razítka vůbec nezapsal update a odmítne se stejně. Ruční úprava se nikdy tiše neztratí; odmítnutí jmenuje soubor a příkaz --diff, který ji ukáže.
  • mode: region (viz SCHEMAS.md) generuje jen text mezi značkami majordomus:begin a majordomus:end. Zbytek cíle se zkopíruje bajt po bajtu, chybějící oblast se jednou připojí a poškozené značky se odmítnou (15). Tak repozitář, který už má ručně psaný CLAUDE.md, přijme Majordomus, aniž by o něj přišel.
  • Připojí projections.updated do ledgeru.
  • Každý cíl v režimu souboru začíná razítkem jmenujícím tento příkaz, hash politiky, z níž pochází, a hash obsahu pod ním; cíl v režimu oblasti nese tytéž dva hashe ve své počáteční značce. Toto razítko je provenience, s níž se doctor a watch porovnávají, na čerstvém klonu stejně jako zde.
  • Vždy načítaná projekce se po generování kontroluje proti rozpočtu; přes rozpočet je 10 a nic se nezapíše. U projekce regionu rozpočet měří generovaný region a doctor hlásí vlastní délku hostitelského dokumentu jako INFO.
  • Spustitelný soubor v Rustu vykresluje stejné cíle ze stejných vstupů, bajt po bajtu: majordomus generate providers je zapisuje a majordomus generate --check (které spouští CI) skončí s 10 a jmenuje každý cíl, který se liší od politiky nebo chybí. update je interaktivní zapisovač s jeho odmítnutími; generate --check je kontrola. Případ testu 93 dokazuje, že se oba shodují v obou směrech.

majordomus handover

Zapsat záznam pokračování jen pro připisování.

Rozlišení záznamu — výběr, který dřívější záznam se týká práce probíhající zde — je stejné pravidlo pro předávky, checkpointy i context: stejný worktree a větev, jinak stejná větev, jinak nic.

Vstup: sepsané tělo na stdin. Povinné sekce jsou handover.required_sections politiky jako nadpisy první úrovně, každá s neprázdným obsahem; zástupné znaky šablony ve špičatých závorkách se počítají jako prázdné. Volitelné: # Completed, # Decisions, # Verification, # Risks, # Open Work.

--derive sestaví tělo místo čtení ze stdin, a to ze záznamu úlohy, ledgeru, gitu, otevřených otázek a nejnovějšího checkpointu — každý z nich je již zapsaný, zvalidovaný a ověřený na identitu. Nevolá žádný model ani nedělá síťový požadavek, a proto ho může bezpečně spouštět hook poskytovatele: odvozené tělo může být chybné jen tehdy, je-li chybný některý záznam, který čte, a každý z nich má vlastní kontrolu. Povinné sekce politiky se vypisují v pořadí politiky; povinnou sekci, kterou tento generátor nedokáže naplnit, odmítne podle jména, místo aby ji zapsal prázdnou, protože prázdná sekce projde kontrolou sekcí a dalšímu workerovi nic neřekne. Ručně napsané tělo stále řekne víc než odvozené a --derive existuje proto, aby nepřítomnost někoho, kdo chce psát, už neznamenala nepřítomnost záznamu.

Zapisuje: jeden nový soubor state/handovers/<utc-ts>--<branch-key>--<short-head>--<rand>.md, režim 0600, vytvořený atomicky (dočasný soubor, pak pevný odkaz; při kolizi opakování s novou náhodnou příponou). Front matter se počítá z gitu a z current.yaml; tělo, které se pokouší nastavit pole identity, se odmítne.

Nikdy nestaguje, necommituje ani neupravuje žádný jiný soubor než checkpoint_at záznamu úlohy. Připojí task.handed_over do ledgeru. Vypíše cestu.

--close navíc nastaví výsledek úlohy na handed_over, aby mohla v tomto checkoutu start nová úloha; starý záznam se tím start archivuje. Bez --close zůstává úloha aktivní, aby v ní pokračovala další relace.

Událost SessionEnd poskytovatele spustí --derive --close, nebo --derive --no-task, když záznam úlohy neexistuje, aby epizoda, která skončí, zanechala záznam pokračování, a ne jen obálku toho, co vytvořila. Oba odpovídají na různé otázky: záznam relace indexuje epizodu a předávka je to, z čeho další worker pokračuje. Neptá se, zda je úloha aktivní — do ADR 0052 se ptala a repozitář, jehož poslední úloha byla předána, přestal na šest dní zapisovat jediný záznam, z něhož by budoucí worker pokračoval, zatímco všechny kontroly zdraví procházely.

Odmítne (10), pokud povinná sekce chybí nebo je prázdná, nebo pokud tělo obsahuje pole identity. Odmítne (12) bez aktivní úlohy, pokud není --no-task.

Resolve: majordomus handover --resolve najde nejrelevantnější dřívější předávku pro aktuální worktree a branch: nejprve stejný worktree a branch, pak stejná branch, nikdy záloha v rámci celého repozitáře. Vypíše štítek stavu gitu a tělo. Žádný kandidát je běžný výsledek a skončí s kódem 0 s No relevant handover. --path vypíše jen cestu, pro skriptování; --no-task rozřeší i bez aktivní úlohy.

majordomus context

Co potřebuje vědět ten, kdo bude pracovat dál? Pouze pro čtení. Sestaví trvalý stav do jednoho briefingu a vypíše ho. Nic se neukládá, nevolá se žádný model a výstup je projekce: každý řádek se při každém spuštění znovu spočítá ze záznamů a gitu.

Pořadí sekcí je pořadí autority. Nejdřív git, protože je jediný, který nemůže být zastaralý; pak úloha a profil, který ji omezuje; pak blokátory, které mění, co lze přijmout; pak ručně psané záznamy; nakonec historie událostí, protože je to nejslabší důkaz o přítomnosti.

SekceZdrojZahrnuto, když
GITgitvždy
TASKstate/current.yamlúloha je aktivní a context.task profilu není false
PROFILEprofiles/<name>.yamlúloha jmenuje profil, který existuje
CONTEXT DOCUMENTS.ai/**/README.md (kontrakt kontextu)úloha je aktivní; pro každou z cest jejího rozsahu se vypíše účinný řetězec
OPEN QUESTIONSstate/open-questions.mdjakákoli nevyřešená položka jmenuje tuto úlohu
DECISIONSstate/decisions.mdcontext.decisions: true (tato úloha) nebo context.architecture_notes: true (repozitář)
REASONINGstate/reasoning/<task>/, vykresleno pomocí majordomus-cli reasoning status --reportúloha má záznamy uvažování a context.decisions: true
LATEST CHECKPOINTstate/checkpoints/pro tuto úlohu se rozliší checkpoint
LATEST COMPATIBLE HANDOVERstate/handovers/předávka se vyřeší pro tento worktree a větev
knowledge candidates.ai/repo/knowledge/candidates/v odvozeném briefingu, za otevřenými otázkami a před předávkou: kandidáti čekající na review, jejichž epizoda byla na této větvi, počet a id, omezeně; nepřiřazení na vlastním řádku; absence se vypíše, ne vynechá
KNOWLEDGE POINTERSgraf znalostí (majordomus knowledge), zkompilovaný při tomto čtenícontext.knowledge: true a úloha pojmenovává úkol: viz níže
FILES TOUCHED IN SCOPEgitcontext.relevant_files: true
RECENT HISTORYstate/ledger.jsonlcontext.recent_history_depth je nad nulou
PROMPTprompts/<name>.md--prompt <name> bylo zadáno

Toto je jediný kód, který čte blok context profilu, a právě to dělá z těchto polí stav, ne dokumentaci.

Rozpočet. context.builder_budget_lines v politice, nebo --budget-lines. Když sestavený text rozpočet překročí, sekce se vypouštějí v pevném pořadí — historie, soubory, rozhodnutí, potom těla kontrolního bodu a předávky, která degradují na odkaz, místo aby zmizela. Git, úloha, profil a blokátory se nevypouštějí nikdy. Každé vypuštění je pojmenováno pod EXCLUDED s důvodem, takže nedostatečně naplněný kontext se ladí ze seznamu vyloučení, ne hádáním. Skončí s 10, pokud je to, co vypustit nelze, už nad rozpočtem.

Ukazatele znalostí. Omezený výřez grafu znalostí, ne graf: jeden řádek na ukazatel — vztah, který jej vybral, druh, id, zdrojová cesta a titul. Výběr sleduje jen hrany, které soubor uvádí, v tomto pořadí, každá úroveň v bajtovém pořadí id: úkol, pro který byla úloha zahájena, úkoly, které depends_on, jeho milník, tvrzení, která ten milník deklaruje, a každý uzel, jehož zdrojem je cesta, kterou úloha nebo úkol jmenuje ve svém scope. Nic se nehledá a nic se neskóruje — dokument, jehož slova se shodují s úkolem, ale na který nic neodkazuje, se nevybere. Seznam se zastaví na context.knowledge_max_items profilu (context.max_list_items politiky, když není nastaveno, a nikdy nad ním) a poslední řádek spočítá zbytek pomocí majordomus knowledge related issue:<id>, který je vytiskne. Sekce chybí a je jmenována pod EXCLUDED s důvodem, když profil nenastavuje context.knowledge: true a když úloha nejmenuje žádný úkol pod politikou, jejíž intent.binding není off; bez úlohy, nebo s vypnutou vazbou a bez úkolu, prostě chybí. Kompilují se jen druhy a cesty, kterých výběr může dosáhnout, takže stojí asi dvě sekundy na plánu o čtyřech stech úkolech místo osmi, které trvá celá kompilace; je to pár řádků z rozpočtu 300 řádků. Když úkol, který úloha jmenuje, není uzel — jeho záznam opustil index správy verzí, nebo zmizel — sekce vytiskne řádek stale s tím, který a proč, a přesto vypíše, co graf drží. Při těsném rozpočtu se zahodí spolu s odvozenými výpisy, po souborech a před kontextovými dokumenty, a tedy dlouho před gitem, úlohou, profilem a otevřenými otázkami, které se nikdy nezahazují; zahození jmenuje příkaz, který ukazatele vytiskne.

--for <provider> obaluje totéž tělo hlavičkou jmenující poskytovatele a jeho vždy načtený soubor. Tělo se nemění: kanonický kontext je neutrální vůči poskytovateli a poskytovatel, který by potřeboval jiné fakty, by byl jiná politika, ne jiné vykreslení.

--json vypíše jeden objekt: git, task, sections[] (každý s id, lines a text), excluded[] (každý s item a reason) a budget. Stejný výběr jako textová forma, protože obě se sestavují jednou a vykreslují dvakrát.

$ majordomus context
# Majordomus context — 2026-09-03T19:41:02Z
# a projection of durable state, not a source of truth: validate every line against git

## GIT
repository   /home/dev/app
branch       main
head         3f2a9c1e4b7d8a05c1119f2b6e0d7a3c8e5f1b42
working_tree dirty
task_record  advanced (recorded head 3f2a9c1)

## TASK
id           t-20260903193012-a4f1
task         fix the OAuth callback
profile      debugging
scope        lib/auth

## OPEN QUESTIONS (1 unresolved — every one refuses finish --outcome completed)
- Does the legacy mobile callback still require the old URI form? (2026-09-03)

## EXCLUDED
- history — profile debugging sets context.recent_history_depth: 50

## BUDGET
41 of 300 lines

Kontextové dokumenty. Stejný příkaz nese podpříkazy scoped contextu, všechny jen pro čtení, všechny přijímající --json; model viz CONTEXT.md a front matter viz SCHEMAS.md.

majordomus context list                       every document: id, path, scope, composition, providers, status
majordomus context resolve <path> [--provider P] [--audience A]
                                              NN  <id>  <path>  scope=… composition=… providers=…   in effective order
majordomus context explain <path> [...]       resolve, plus one line per document saying why it is in
                                              (ancestor at depth N, tracks, directory) and every filtered
                                              or superseded document with its reason
majordomus context validate                   the whole tree; findings in the usual format; exit 10 on any FAIL
majordomus context affected [--base <ref>|--staged|--worktree]
                                              which documents and scopes a change set touches; a tracked
                                              path that changed is a WARN naming the document, never the exit code
majordomus context check-sync [--base <ref>]  validate, projections up to date, affected review items;
                                              invalid tree FAIL exit 10, hand-edited projection DRIFT exit 11,
                                              absent projection INFO (doctor owns missing), otherwise 0

Cílem resolve, explain a holého briefingu je adresář relativní k repozitáři (soubor se rozřeší na svůj vlastní); cesta, která ještě neexistuje, se rozřeší od svých předků, jako soubor ve svém rodiči, pokud není zapsána s koncovým /, a řekne to; symlink nebo .., který uniká z repozitáře, je odmítnut (15, refused-path). Uvnitř stromu .ai/ je výsledkem řetěz předků připuštěný rozsahem každého dokumentu; vně něj kořenový řetěz plus každý dokument, jehož tracks cíli odpovídá. Pořadí je hloubka, pak order, pak cesta. Briefing získává sekci CONTEXT DOCUMENTS s výpisem účinného řetězu pro cesty rozsahu aktivní úlohy.

Třídy chyb, použité doslova ve zprávách a v --json: invalid-front-matter, unsupported-schema, unknown-key, duplicate-id, broken-reference, cycle, illegal-override, unknown-provider, invalid-manifest, stale-projection, out-of-sync, refused-path.

majordomus session

Otevřít, prohlédnout a zavřít jednu epizodu provádění. Relace je sedmý trvalý záznam a jediný, který nemá tvar úlohy.

Úloha je jednotka práce: má rozsah, má profil a může přežít pracovníka, který ji dělá. Relace je jedno sezení jednoho pracovníka: nenárokuje žádné cesty, nekontroluje přijetí a může překročit několik úloh — zatímco jednu úlohu může překročit několik relací. Ani jedno neobsahuje druhé, proto jsou to dva záznamy, a ne jedno pole.

Relace jsou volitelné. Worker, který žádnou neotevře, přijde o hranici epizody a o nic jiného; každý další záznam se zapisuje přesně jako dřív.

  • start [--owner <who>] [--worker <id>] [--if-open refuse|keep] otevře epizodu. Jedna otevřená relace na worktree: druhé start se odmítne místo nahrazení první, pokud --if-open keep neříká, že se má ponechat to, co je otevřené — což je to, co předává hook poskytovatele, protože jeho událost start se spustí znovu při obnovení i při kompakci. --worker je volný řetězec identity, zaznamenaný, jen když je zadán — nezaznamenaný pracovník zůstane nezaznamenaný, protože uhodnutý je ve chvíli zápisu k nerozeznání od zaznamenaného. --provider a --provider-session zaznamenávají, událost kterého poskytovatele epizodu otevřela, a vlastní identitu relace tohoto poskytovatele; může je dodat jen něco, co běží uvnitř hooku onoho poskytovatele, a právě to činí opened_by: hook v pracovním kontextu faktem, ne tvrzením.
  • status vypíše otevřenou relaci se štítkem divergence commitu, na němž se otevřela, nebo oznámí, že žádná není. Pouze pro čtení. Nepřítomnost je odpověď, ne selhání.
  • close [--outcome closed|interrupted] [--if-none refuse|ignore] uzavře epizodu do neměnného záznamu v sekci relací vrstvy a odstraní otevřený. Na stdin může přijít autorské shrnutí a je volitelné; identifikační pole v něm se odmítají, stejně jako v checkpointu. --if-none ignore dělá z „nic nebylo otevřeno“ běžný případ místo selhání, což je to, co předává koncová událost providera.
  • context [<session-id>] vypíše cestu k pracovnímu kontextu epizody. Pouze čtení a vypisuje cestu, ne dokument: dokument je lokální důkaz a příkaz, který ho vylije do terminálu, zve ho do něčího kontextu.

Otevření zmrazí kontext, který dostalo. start zapíše .ai/local/session-contexts/<stamp>--<session-id>.md — úvodní část epizody, výstup tvůrce kontextu doslova a sekci ## Notes pro pracovníka — a close připojí sekci ## Close jmenující výsledek a záznam. Do dokumentu se jen připisuje a nikdy se nepřepisuje, takže to, co do něj pracovník napsal, přežije. Úložiště je lokální: jmenuje tento stroj a je snímkem projekce, takže se nikdy nepublikuje ani samo nenačítá do kontextu a doctor odmítne dokument, který nese konverzaci. Hooky, které činí hranici nezávislou na tom, zda si ji někdo pamatuje vést, viz capture session.

Uzavřený záznam je obálka odkazů. Jmenuje úlohy, úkoly, milníky, checkpointy, předávky, rozhodnutí, otázky a důkazy epizody a nekopíruje tělo žádného z nich. Nese také commity mezi otevíracím a uzavíracím commitem — nebo jediný záznam diverged, když otevírací commit už není předkem, protože seznam spočítaný napříč historií, která už nesouvisí, je fikce.

Seznamy se odvozují při uzavření, nehromadí se, dokud je relace otevřená. Žádný jiný příkaz neví, že relace existují: checkpoint, decision, question a plan zůstávají beze změny. Odkazy se čtou z ledgeru, který už je jen pro připojování, už ho zapisuje jen Majordomus a už je validován.

Výběr se dělá podle razítka relace na každém řádku ledgeru, ne podle časového rozsahu. Každý řádek nese relaci, která jej zapsala, vedle commitu a větve, které už nesl. Časový rozsah byl implementován jako první a při prvním spuštění se ukázal jako chybný: ledger je jeden soubor na repozitář, dva workeři do něj zapisovali a žádné časové razítko je neodlišuje, takže obálka jedné epizody si nárokovala úlohy, checkpointy a předávky té druhé. Řádek bez relace nepatří žádné epizodě — relace jsou volitelné a práce mimo ni se nepřipisuje nikomu, místo aby se připsala tomu, kdo měl poblíž otevřenou relaci.

--outcome přijímá closed nebo interrupted. Obojí je sdělováno samotným autorem a nic se neověřuje; interrupted existuje, protože „tato epizoda byla přerušena a její záznamy mohou být neúplné“ je jediná věc o skončené relaci, která mění, co kdo udělá dál.

Kód 12 bez otevřené relace, 10 když souhrn nese pole identity, 15 když otevřený záznam patří jinému checkoutu.

  • list [--all] vypíše uzavřené epizody, nejnovější první, s označením odchylky u každé.
  • show <session-id> vypíše jeden záznam celý.
  • latest [--path] vypíše nejnovější záznam, který se rozřeší pro tento worktree a větev.

Všechny tři jsou jen pro čtení a všechny tři vypisují štítek divergence záznamu — exact, advanced, diverged, different_context. Nevymýšlí se druhý slovník pro zastaralost, protože relace napsaná před přepsáním větve a předaná dalšímu pracovníkovi, jako by stále popisovala tuto historii, je přesně to, čemu mají těch čtyři slova zabránit.

Řešení je pravidlo, které sledují všechny ostatní záznamy: stejný repozitář, stejný worktree a větev, pak stejná větev, pak nic. Záznam z nepříbuzného worktree se nikdy nenabízí — vypůjčený kontext nelze poznat jako chybný, dokud se podle něj nejednalo. --all pravidlo výslovně uvolní a ukáže větev každého záznamu, protože záznam odjinud stojí za zobrazení, když jste žádali o všechno, a nikdy za tiché podání.

Řadí se podle zaznamenaného časového razítka, přičemž pozice v knize rozhoduje o shodě uvnitř jedné sekundy. Čas úpravy souboru se nikdy nečte: nepřežije klon a není to čas, který záznam tvrdí, takže dotyk starého záznamu z něj neudělá nejnovější. Pořadí názvů souborů se obvykle shoduje s pořadím knihy, díky čemuž implementace, která sklouzla k názvu souboru, vypadá správně; test/cases/62_session_divergence.sh je záměrně rozejde a selže, když o pořadí nerozhoduje kniha.

Poškozený záznam se přeskočí s varováním na stderr, nikdy mlčky a nikdy fatálně: jeden nečitelný soubor nesmí stát celý výpis.

Jedna epizoda na relaci poskytovatele, ne jedna na worktree. Dvě okna téhož poskytovatele otevřená na jednom checkoutu jsou dva pracovníci. Jejich sloučení do jednoho záznamu označilo řádky knihy jednoho pracovníka id druhého a nechalo koncovou událost kteréhokoli okna zavřít epizodu pro oba; změřeno v tomto repozitáři 2026-09-09, sedm souběžných relací mezi sebou vyprodukovalo jeden záznam. Otevřená epizoda je proto vlastní soubor, state/sessions-open/<provider session>.yaml, klíčovaný relací poskytovatele, která ji otevřela. --if-open keep ponechává vaši epizodu — proto existuje: startovní událost se spouští znovu při obnovení a při kompakci — a jiné relaci poskytovatele dává vlastní epizodu. close --provider-session <id> zavře tuto epizodu a žádnou jinou, což je to, co předává koncová událost poskytovatele. Epizoda, kterou nikdo nepojmenoval, je klíčována hand a je nejvýše jedna: poskytovatel, který neposílá identitu relace, je nerozlišitelný od člověka u terminálu a vymýšlet tam rozdíl by množilo epizody, které nikdo nezavře. Ručně otevřená epizoda nepotřebuje nikde žádného poskytovatele a chová se přesně jako dřív.

state/session-current.yaml je relativní symlink do toho úložiště: ukazatel na epizodu tohoto checkoutu, zaměřený na tu, která byla otevřena zde naposledy, a znovu zaměřený uzavřením na tu, která zůstala otevřená, je-li právě jedna. Kterým epizodám patří příkazy daného procesu, se rozhoduje v mj_session_here_file (lib/common.sh): relace poskytovatele uvedená na příkazové řádce, jinak relace poskytovatele, ve které tento proces běží, je-li zde otevřena epizoda s takovým klíčem (MAJORDOMUS_PROVIDER_SESSION, nebo proměnná, kterou deklaruje adaptér životního cyklu — CLAUDE_CODE_SESSION_ID pro Claude Code), jinak ukazatel. Dvě otevřené epizody a proces, který nemůže jmenovat ani jednu, je jediný případ, který nelze vyřešit: ukazatel jmenuje naposledy otevřenou a session status vypíše ostatní pod Also open:, místo aby nechal hádání projít jako skutečnost.

Zapisuje: state/sessions-open/<provider session>.yaml, režim 0600, zapsáno atomicky, ukazatel vedle něj a jeden řádek session.started v knize. session_id, repository_id, worktree, branch, start_head a start_working_tree se počítají z gitu a nikdy se nepíší. provider a provider_session se zaznamenávají jen tehdy, když je dodal hook providera, což je také to, co je činí hlásitelnými: continuity.state deklaroval OpenSession.provider na rok, který žádný zapisovatel nemohl vytvořit.

Nic pod .ai/local/state/ není sledováno, včetně záznamů otevřených relací: otevřená relace nenese nic, co by kdokoli jiný potřeboval, a záznam, který by dorazil z jiného checkoutu, by tomuto dal zdědit epizodu, kterou neotevřel. Záznam, který jmenuje jiný worktree, se hlásí a nikdy se neuposlechne, což drží obranu na místě pro každý způsob, jímž může stále dorazit — zkopírovaný pracovní adresář, synchronizovaná složka.

Exit 15, když je vaše vlastní relace zde už otevřena, 10, když se záznam neparsuje — poškozený záznam selže nahlas, místo aby se četl jako „žádná relace“, protože čtení jako nepřítomného by druhému start umožnilo přepsat ho.

$ majordomus session status
No open session in this worktree.
next: majordomus session start

$ majordomus session start --worker some-provider/some-model
session s-20260904153733-fc51 opened at 2026-09-04T15:37:33Z (head 9c13909)
next: majordomus plan next; majordomus context; majordomus session close when the episode ends

$ majordomus session start
majordomus: session s-20260904153733-fc51 is open here since 2026-09-04T15:37:33Z; run majordomus session close first

$ majordomus session close <<'EOF'
The extraction boundary and the session schema landed; the compiler's discovery stage is next.
EOF
.ai/repo/sessions/20260904T171402Z--s-20260904153733-fc51--master--3c9ba2f--c0ffee1234567890.md

majordomus evidence

Zaznamenat důkazy proti aktivní úloze: jeden závazek, který deklarovala, splněný, nebo jedna validační kontrola modelu CI, která podala zprávu.

majordomus evidence --covers <token> [--type <kind>] (--command <cmd> | --artifact <ref>) [--result <r>] [--json]
majordomus evidence --gate <id> --exit <status> [--command <cmd>] [--result <r>] [--json]
majordomus evidence <show|claim|proves|record|stamp> [options]

Třetí forma patří druhému programu. Každý argument, který tento příkaz přijímá, je volba, takže slovo na první pozici nikdy není jeho vlastní: show, claim, proves, record a stamp jsou podpříkazy spustitelného souboru v Rustu, který drží knihu toho, co proběhlo (EVIDENCE.md), a launcher mu je předá se všemi argumenty beze změny — včetně --repo na kterékoli straně příkazu. Jejich návratové kódy jsou kódy spustitelného souboru a nepotřebují aktivní úlohu. Která slova to jsou, se čte z vlastní deklarace spustitelného souboru (docs/generated/cli.yaml v checkoutu, help evidence <word> dodaného spustitelného souboru v nainstalovaném stromu), takže majordomus evidence --help vypíše přesně ty podpříkazy, které tato instalace dosáhne. Slovo, které nemá žádný z programů, se zde odmítne s kódem 2. Zbytek této sekce je o prvních dvou formách.

Obě formy zaznamenávají různé druhy faktů a nikdy nejsou jedním voláním. Povinnost je slib, který úloha dala; kontrola je to, co o stromu řekla validační pipeline. Řádek, který by se tvářil jako obojí, by se nedal číst jako ani jedno, takže zadání --covers a --gate zároveň skončí kódem 2.

scope úlohy říká, kam smí pracovník zapisovat; její requires říká, co pracovník dluží, než bude dostupný výsledek completed. Tokeny jsou deklarovány v share/obligations.yaml — implementation, tests, docs, generated, rules, commit, push, target, pages, deploy, verify — každý jmenuje příkaz, který jej splácí, a tam, kde je fakt lokální, pathspecs, nad nimiž se berou jeho důkazy.

Čtyři z nich se zde nikdy nezaznamenávají. commit, push, target a pages jmenují fakta, která nástroj dokáže zjistit — čistý strom, remote-tracking ref, který dosáhne hlavy, kmen, který ji dosáhne, publikovaný web, který ji servíruje — a check a finish je rozhodují živě na HEAD, místo aby po pracovníkovi chtěly jejich přepis. Zaznamenat takový token nemá žádný účinek: zjištěný závazek se splácí tím, že je pravdivý, a odmítá se tím, že je nepravdivý. Kde to checkout nemůže rozhodnout (žádný remote, žádná zaznamenaná výchozí větev, web, který nikdy neodpověděl), token se vrací k zaznamenanému řádku a nález říká, o který šlo. deploy a verify zůstávají zaznamenávány ručně a share/obligations.yaml jednou řádkou říká proč: nasazení je fakt o stroji, který tento repozitář nikdy neoslovuje.

--command nebo --artifact je povinné: vyprávění není důkaz. Token, který slovník nedeklaruje, skončí s 2; token, který aktivní úloha nikdy neslíbila, skončí s 15, protože zaznamenávat důkazy pro něco, o co nikdo nežádal, je způsob, jak checklist roste o položky, které nikdo nechtěl.

Zapisuje: řádek task.evidence do knihy, nesoucí povinnost, způsob jejího splnění, příkaz nebo artefakt a hash sledovaných souborů, které povinnost jmenuje. Nezapisuje se nic jiného; kniha je už jen pro připisování, uspořádaná a kontrolovaná na integritu a její obálka už nese hlavu, větev a relaci.

Právě ten hash je podstatou. Důkaz splácí závazek, jen dokud se přepočtený hash jeho vstupů rovná zaznamenanému, takže změna kteréhokoli souboru, který závazek jmenuje, důkaz odebere, místo aby jej nechala být — táž otázka aktuálnosti, kterou klade vlastní source_hash webu, položená výsledku testu. Závazek, jehož fakt je vzdálený (push, integrace, publikace, nasazení), nemá žádné vstupy a je vázán na commit, v němž byl pořízen, posuzovaný exact | advanced | diverged | different_context jako každý jiný zdejší záznam.

check a finish vyhodnocují každou povinnost při každém běhu přes doktrínu majordomus.obligation-closure, takže zastaralý důkaz je vidět dřív, než na něm někdo staví; odmítnut je jen výsledek completed. Worker, který hlásí blocked, je upřímný, a jeho odmítnutí by ho naučilo tvrdit raději completed.

Zaznamenání kontroly

--gate jmenuje kontrolu z .ai/repo/ci/gates.yaml, modelu CI tohoto repozitáře, a --exit stav, který ohlásila; 0 je úspěch. Kontrola, kterou model nedeklaruje, končí kódem 2, stejně jako --exit, které není číslo.

majordomus evidence --gate rust-check --exit 0 --command 'scripts/rust-check --ci'

Zapisuje: řádek task.gate do knihy, nesoucí kontrolu, její návratový kód a hash souborů, které ji vybírají. Ty soubory se odvozují z modelu, nedeklarují se: sjednocení cest každé třídy, která kontrolu pojmenovává, a vše pro kontrolu, kterou vybírá každý plán, protože kontrola, kterou vybírá každý plán, je kontrola, kterou může zneplatnit jakákoli změna. Změňte jeden z nich a běh přestane kontrolu splňovat — zacommitováno či ne, protože sada změn, nad níž se úloha posuzuje, zahrnuje její pracovní strom.

check a finish vyhodnotí každou kontrolu, kterou vybere vlastní sada změn úlohy, přes doktrínu majordomus.completion-gates, a každou hlásí v jednom slovníku:

statusvýznam
passproběhlo nad těmito vstupy a skončilo s kódem 0
failběželo nad těmito vstupy a neběželo
staleběžela a soubory, které ji vybírají, se od té doby změnily
blockedněco, bez čeho nemůže běžet, neprošlo
queuedplán jej vybere a žádný běh dosud nehlásil
exemptnic, co tato změna udělala, to nemůže učinit pravdivým ani nepravdivým
unknownnelze ho zde vůbec posoudit — model, který se nenaparsuje, čtečka, která není sestavena, chyby nebo žádná odpověď, žádné jq

fail, stale, blocked a unknown odmítnou výsledek completed: completed je tvrzení, že verdikt je znám, a verdikt, který nešlo přečíst, jej nemůže podpořit. Každý jiný výsledek je přijat i nad kterýmkoli z nich, označený „not refused“, a check hlásí, aniž by odmítal. Repozitář, který nedeklaruje žádný model CI, nemá žádnou kontrolu, o které by mohl být verdikt neznámý, a nic neodmítá. queued se hlásí jménem, nikdy se nepřijme jako pass a nikdy se neodmítne: verdikt, který nikdy nepřišel, a verdikt, který řekl pass, jsou různé skutečnosti, a 2026-09-10 nesl trunk tohoto repozitáře přes noc tři vady rozbíjející branch, protože vypadaly stejně (majordomus.never-reported-is-not-green).

Celý verdikt — každá kontrola, plán, který ji vybral, které závazky změna implikuje, a dvaadvacet otázek invariantu hotovo se zdrojem, který každou zodpověděl — je jeden dokument, gates.completion, čtený stejně příkazovou řádkou, HTTP API, MCP i Cockpitem:

majordomus-cli run gates.completion --input '{}' --format json | jq '.output.questions'

Závěrečné otázky

Tři z těchto otázek jsou ty, které si člověk klade, když relace končí, a pracovník není jejich zdrojem (project.a-closing-question-is-answered-by-measurement). Každá se čte ze záznamu, který drží daný fakt, měřeno od začátku úlohy:

otázkaptá sečteno zkdyž to není pass
no-new-debtnení zaznamenaný dluh větší než při zahájení úlohy?položky každého .ai/repo/*-baseline.txt v commitu, na kterém úloha začala, a v pracovním stromufail, jmenuje každý základ, který vzrostl, a o kolik
nothing-accumulatedzmizela znovu každá větev a worktree, které od té doby vznikly?reflogy gitu: kdy začala každá lokální branch a každý připojený worktreequeued, s uvedením toho, co zbývá, a se součty nyní
backlog-not-grownbyl od té doby uzavřen každý otevřený pull request?zaznamenané pozorování forge (majordomus prs refresh)queued, s jejich očíslováním; unknown, když forge nebyl od zahájení úlohy pozorován

Jsou to čtení repozitáře, ne jednoho pracovníka: git neříká, kdo větev vytvořil, takže větev, kterou vytvořila jiná relace od začátku úlohy, se počítá. Čtení, které nešlo provést, je unknown a říká proč; nikdy to není pass. majordomus check pojmenovává každou dosud dluženou otázku jako id=status na jejím řádku done, takže odpověď na „je vše hotovo a nezanechalo to repozitář větší, než ho našlo?“ je jeden příkaz, ne vzpomínka; důkazy za každým slovem jsou v questions dokumentu výše.

majordomus checkpoint

Zaznamenat stručný postup uvnitř aktivní úlohy. Pouze pro připisování; tělo přichází na stdin.

Checkpoint není malá předávka. Předávka je záměrný balíček pro pokračování, který se píše, když worker končí; checkpoint je to, co platilo před chvílí, dost krátké na to, aby ho kontext dalšího workera mohl citovat celý. checkpoint.max_body_lines v politice ten rozdíl vynucuje — tělo nad limitem se odmítne s doporučením napsat místo toho předávku, a neořízne se.

Zapisuje: state/checkpoints/<ts>--<branch>--<head>--<rand>.md, režim 0600, vytvořeno atomicky s link, nikdy nestageováno. Front matter se počítá přesně jako u předávky; tělo obsahující identitní pole je odmítnuto. Také aktualizuje checkpoint_at v záznamu úlohy a připojí task.checkpoint do knihy.

Prázdné tělo je povoleno a nezapíše žádný soubor: aktualizuje jen checkpoint_at, což check --checkpoint dělalo vždy. Obojí je tatáž operace; checkpoint je ta, která umí říct i to, co platilo.

  • --derive skládá tělo z gitu a ledgeru, místo aby četl stdin: commit, pracovní strom, kolik souborů se od začátku úlohy změnilo, commity od té doby a počet blokátorů. Zůstává uvnitř stejného limitu a je to, co spouští událost kompakce providera — kompakce zahodí konverzaci, zatímco práce pokračuje, a okamžik, kdy je oznámena, je jediný okamžik, kdy lze cokoli zapsat o kontextu, který přestane být dosažitelný.
  • --show vypíše nejnovější checkpoint pro tento worktree a větev — pro aktivní úlohu, je-li nějaká, jinak nejnovější bez ohledu na to, kterou úlohu jmenuje.
  • --list vypíše checkpointy tohoto worktree, nejnovější první, s gitovým štítkem každého.

Checkpoint patří epizodě, ne úloze. Zapisuje se, ať je úloha otevřená, nebo ne, a ať poslední dosáhla jakéhokoli výsledku; task_id záznamu je úloha, když nějaká je, a jinak none, a událost knihy pole zcela vynechá, místo aby zapsala doslovné none, které by každý čtenář té události sebral jako úlohu toho jména. Jen checkpoint_at samotné úlohy se při skončení úlohy nechává být, protože toto pole patří úloze.

Tohle se odmítalo s 12 (žádná aktivní úloha) a 15 (úloha už není aktivní) až do ADR 0052. Ta dvě odmítnutí jsou výpadek z 2026-09-05: úloha označená handed_over a nikdy nenahrazená umlčela záznamy o pokroku tohoto repozitáře na šest dní, zatímco epizody se dál otevíraly a zavíraly a každá kontrola zdraví procházela.

Kód 10, když tělo nese pole identity nebo překročí strop.

$ majordomus checkpoint <<'EOF'
OAuth state mismatch reproduced with the fixture in test/fixtures/callback.json.
Cause is in callback normalisation, not in the comparison.
Next: regression test before touching the implementation.
EOF
.ai/local/state/checkpoints/20260903T194500Z--main--3f2a9c1--8c1d0e4a2b6f9317.md

majordomus history

Přečíst knihu pouze pro připisování zpět. Pouze čtení.

Provozní rekonstrukce, ne přepis. Odpovídá, co se stalo, kdy, pro jakou úlohu, na jaké hlavě gitu a co bylo přijato — a nic o tom, co kdo řekl.

Filtry: --task <id>, --event <name>, --since <n>m|h|d nebo časové razítko ISO, --limit <n> (výchozí 20, nejnovější), --all. Výstup je od nejstaršího řádku, takže filtrovaný běh se čte jako vyprávění. --json vypíše odpovídající řádky knihy doslovně.

--validate hlásí každý řádek, který není dobře utvořenou událostí, a skončí s kódem 10, pokud nějaký je; doctor, check a watch provádějí stejný test, protože kniha, kterou nástroj neumí zpracovat, je kniha, kterou nelze použít jako důkaz.

--rotate přesune všechny řádky kromě nejnovějších ledger.retention_max_lines do ledger.<utc>.jsonl.archived a připojí událost ledger.rotated zaznamenávající, kolik se jich přesunulo. Nikdy nemaže, odmítne přepsat existující archiv a nedělá nic, když je kniha pod stropem.

$ majordomus history --task t-20260903193012-a4f1
2026-09-03T19:30:12Z  task.started         t-20260903193012-a4f1  3f2a9c1  profile=debugging scope=lib/auth
2026-09-03T19:45:00Z  task.checkpoint      t-20260903193012-a4f1  3f2a9c1  20260903T194500Z--main--3f2a9c1--8c1d0e4a2b6f9317.md
2026-09-03T19:52:31Z  decision.recorded    t-20260903193012-a4f1  3f2a9c1  Normalise the callback URI before comparing state
2026-09-03T20:14:08Z  task.finished        t-20260903193012-a4f1  b71e0c9  outcome=completed verify_exit=0

majordomus decision

Zaznamenat nebo číst trvalá rozhodnutí. Jeden soubor jen pro připisování: state/decisions.md.

decision add "<what>" --why "<why>" připojí položku s id úlohy a spočteným git headem. --why je povinné: rozhodnutí bez zaznamenaného důvodu nelze později přezkoumat, jen znovu obhajovat. --rejected a --evidence jsou volitelné a výchozí je -.

Záznam se nikdy neupravuje ani nemaže. --supersedes "<text>" zaznamená, že pozdější rozhodnutí nahradilo dřívější. Text se porovnává jen s tituly zaznamenaných rozhodnutí, musí identifikovat právě jedno z nich a zaznamená se právě tento titul; text, který neodpovídá žádnému titulu nebo odpovídá více než jednomu, se odmítne, takže nahrazení vždy míří na jedno skutečné rozhodnutí. decision list označí nahrazený záznam pomocí Superseded by: <title>.

decision list [--task <id>] [--limit <n>] vypíše záznamy od nejnovějšího; decision show "<text>" vypíše první záznam, jehož název obsahuje daný text, nebo skončí s 12.

Profil deep-work nastavuje verification.decision_record_required: true; finish pak odmítne completed, pokud některý záznam nepojmenuje úlohu.

$ majordomus decision add "Normalise the callback URI before comparing state" \
    --why "the mismatch is a trailing-slash difference, not a forged state parameter" \
    --rejected "relaxing the comparison, which would accept genuinely forged states" \
    --evidence "test/auth/callback_test.exs:41"
recorded: Normalise the callback URI before comparing state

majordomus question

Otevírat, řešit a vypisovat otázky, které blokují přijetí. Jeden měnitelný index: state/open-questions.md.

Nevyřešené otázky jsou explicitní stav, ne próza uvnitř předávky, protože finish --outcome completed odmítá, dokud některý záznam pojmenovává aktivní úlohu. Tato kontrola je důvodem, proč má soubor strojově psaný formát řádků, a důvodem, proč check, doctor a watch selžou na záznamu, který se nenaparsuje: kontrola, která záznam nepřečte, je kontrola, kterou lze obejít překlepem.

  • question add "<question>" připojuje - [unresolved] <task id> — <question> (<date>).
  • question resolve <n|"<text>"> --answer "<answer>" přepíše ten jeden řádek na [resolved <date>] a připojí odpověď. n je číslo, které vypsal question list. --answer je povinný a nejednoznačný selektor se odmítne, ne hádá.
  • question list [--all] [--task <id>] ukáže každou nevyřešenou položku, protože každá z nich zde odmítá dokončený finish; --task zúží na to, co otevřela jedna úloha, a --all přidá vyřešené. Číslování je to, co vybírá question resolve <n>. Vyřešit lze jakoukoli nevyřešenou otázku, nejen tu, kterou otevřela aktivní úloha: kontrola, kterou nikdo nemůže odstranit, je kontrola, která se obchází.

Vyřešení upravuje index, protože index toho, co je stále otevřené, se nesmí hromadit. Záznam jen pro připojování každého otevření a vyřešení s jeho odpovědí je ledger.

$ majordomus question add "Does the legacy mobile callback still require the old URI form?"
opened for t-20260903193012-a4f1: Does the legacy mobile callback still require the old URI form?
$ majordomus finish --outcome completed --verify-command "mix test"
FAIL blockers   t-20260903193012-a4f1 — unresolved entry in open-questions.md  [reproduce: majordomus question list]
finish: refused, 1 unmet

majordomus prompt

Vypsat, zobrazit a vykreslit prompt assety lokální v repozitáři. Pouze pro čtení.

Asset je .ai/repo/prompts/<name>.md: YAML front matter s name (shodným s názvem souboru) a neprázdným description, pak tělo. Jsou malé, verzované spolu s repozitářem a nezávislé na poskytovateli — knihovna promptů není cíl a nic zde nevolá model.

Vykreslení nahrazuje uzavřenou sadu tokenů a žádné jiné. V textu: {{TASK}}, {{TASK_ID}}, {{PROFILE}}, {{SCOPE}}, {{OWNER}}, {{BRANCH}}, {{HEAD}}, {{WORKING_TREE}}, {{REPOSITORY}}, {{NOW}}. Samostatně na řádku: {{OPEN_QUESTIONS}}, {{DECISIONS}}, {{CHECKPOINT}}, {{HANDOVER}}, {{CONTEXT}}.

Žádný šablonovací jazyk neexistuje: žádné podmínky, smyčky, includes ani shell. Neznámý token je chyba, stejně jako neznámý konfigurační klíč, protože prompt, který tiše vykreslí {{TSAK}} jako doslovný text, je horší než ten, který odmítne. doctor a watch validují každý asset ze stejného důvodu.

$ majordomus prompt list
continue               resume a task from durable state instead of from someone's memory
debug                  frame a defect so that the fix is proven, not asserted
handover               produce a continuation record body for the current task
review                 review the working diff against the claimed scope and the finish contract
$ majordomus prompt render debug | head -3
Task t-20260903193012-a4f1 on branch main at 3f2a9c1e...: fix the OAuth callback

Scope: lib/auth

majordomus skills

Dovednosti repozitáře: poskytovatelsky neutrální postupy, každý pro jeden vymezený druh práce, v sekci skills vrstvy. Pouze pro čtení.

Dovednost je adresář .ai/repo/skills/<id>/ obsahující SKILL.md — YAML front matter splňující share/schemas/majordomus/skill/skill.v1.schema.json nad tělem v Markdownu, které je postupem — a volitelně examples/*.md. Nic ji neregistruje. Třída zdrojů skill v .ai/repo/knowledge/sources.yaml ji objeví, a to je táž deklarace, kterou indexuje spustitelný soubor v Rustu, takže dovednost existuje pro skills list, pro doctor, pro web a pro MCP (majordomus://skill/<id>), nebo pro nic z toho. Smlouvu o souboru viz SCHEMAS.md.

majordomus skills list [--json]        every skill: id, status, version, description
majordomus skills show <id> [--json]   the repository-relative path, then the file as written
majordomus skills check [--json]       validate every skill and every reference it makes
  • list vypíše jeden řádek na každou objevenou dovednost v pořadí objevení, včetně neplatných (výpis, který by tiše zkrácen, by skryl soubor vyžadující opravu). --json přidá URI, tagy, související id, vstupy, výstupy, cestu, hash obsahu a sledované příklady.
  • show vypíše cestu na prvním řádku a soubor pod ní; --json přidá tělo jako pole. Id, které není dovednost, skončí s kódem 12 a uvede skills list.
  • check ověřuje každou dovednost proti seznamu povolených klíčů generovanému ze schématu (žádný neznámý klíč), schema: skill/v1, celému číslu version, hodnotě status z uzavřené množiny, id shodnému s názvem adresáře, neprázdným sekcím # Purpose, # Procedure a # Output; odmítne dvě dovednosti nárokující jedno id, dvě dovednosti, jejichž popisy je nelze odlišit, id related, které nepojmenovává žádnou dovednost, a příklad bez nadpisu první úrovně. Popisy se porovnávají po převodu na malá písmena, se sloučenými posloupnostmi mezer a bez koncové interpunkce věty, takže rozdíl musí být v tom, co popis říká, ne v tom, jak je napsán. Každý nález uvádí soubor a každý důvod. Končí počty toho, co prověřil — dovednosti, příklady, reference — a při jakémkoli selhání skončí s kódem 10. Repozitář bez dovedností je WARN, nikdy ne průchod přes nic. Chybějící seznam povolených klíčů (share/allow/skill.txt, distribuce, která nebyla vygenerována) je 13 a uvádí majordomus generate allow.

doctor a watch provádějí stejné zkoumání přes doktrínu majordomus.skill-integrity; scripts/generate-site-data čte stejný katalog a odmítne sestavit web z dovednosti, která neprojde validací.

$ majordomus skills check
OK   skill       1 skill(s) — every one parses, matches its directory and carries its sections
OK   skill       5 reference(s) — every related id and every example resolves
skills: 1 discovered, 1 valid; examples: 5; references: 5 checked; failures: 0

majordomus capture

Zaznamenávat surové prompty člověka z hooku poskytovatele — jako záznam JSON s jeho vykreslením v Markdownu a YAML — vést hranici epizody z vlastních hooků životního cyklu poskytovatele, tyto hooky instalovat a hlásit, co každý poskytovatel v tomto repozitáři skutečně dělá.

Proč hook a ne instrukce. Po pracovníkovi nelze chtít, aby zaznamenával vlastní prompty. Nikdy nevidí bajty, které člověk napsal, jen to, co z nich provider sestavil, a záznam napsaný modelem postrádá právě ty prompty, na kterých záleželo: první z relace, který přijde dřív, než byla přečtena jakákoli instrukce, a každý, při němž byl model zaneprázdněn tím, o co byl požádán. Řádek v AGENTS.md je žádost, ne mechanismus, a žádný behaviorální test nemůže dokázat, že byla žádost splněna. Zachycení proto probíhá pod modelem, v hooku samotného providera, kde je jeho spuštění důkazem, že funguje.

capture install zapíše shim pro každou událost a odpovídající položky do .claude/settings.json a odmítne kterýkoli z nich přepsat: majordomus-capture pro UserPromptSubmit, majordomus-session-start pro SessionStart a majordomus-session-end pro SessionEnd. Když konfigurace existuje a jednu z nich nejmenuje — včetně konfigurace, kterou tento nástroj zapsal dřív, než znal události životního cyklu — příkaz vypíše položky k přidání, jednu na každou chybějící událost, a skončí kódem 15 místo přepsání souboru, který nezapsal. Shim najde repozitář ze svého vlastního umístění: provider dosazuje svůj adresář projektu do řetězce příkazu textově, takže prostředí repozitář nijak nejmenuje a pracovní adresář, v němž hook běží, není zaručen.

capture prompt čte jeden JSON payload ze stdin a zapíše jeden záznam. Nikdy nekončí kódem 2, protože v UserPromptSubmit tento kód ukončení odmítne prompt uživatele a rozbitý archiv nesmí nikoho stát jeho vstup; payload, který nelze přečíst, se místo toho zapíše do vlastního logu archivu — a doctor selže, dokud je tento log neprázdný, protože neúspěšný záchyt znamená ztracené prompty. Tento log je jediné, co zachytí selhání, na kterém záleží: pokud provider přejmenuje pole, v němž prompt přichází, hook stále běží a self test stále projde, protože posílá payload vyrobený samotným nástrojem. Přečtěte log a smažte ho; je to diagnostika a na rozdíl od záznamu se jeho odstraněním nic neztratí. Samotný text promptu je surový úsek z payloadu, zkopírovaný stále v escapované podobě, takže žádný krok dekódování a opětovného kódování nemůže ztratit znak.

Jeden prompt jsou dva soubory pod jedním kmenem, .ai/local/prompts/YYYYMMDDHHMMSS-<slug>.json a .md, kde slug je začátek samotného promptu:

.ai/local/prompts/
  20260905113342-why-arent-prompts-saved-automatically.json
  20260905113342-why-arent-prompts-saved-automatically.md
  20260905114501-make-it-a-file-per-prompt.json
  20260905114501-make-it-a-file-per-prompt.md

Vždy oba formáty, a nejsou alternativami. .json je záznam: vlastní úseky providera, stále escapované, pěkně vypsané po jednom členu na řádek — to, z čeho se odvozuje vše ostatní a co záchyt nesmí ztratit. Záznam čtou i lidé a jediný čtyřicetikilobajtový řádek žádný editor ani diff užitečně nezobrazí; odsazení leží mimo úseky řetězců, takže bajty promptu zůstávají nedotčeny. .md je tento záznam vykreslený pro člověka v jediném tvaru, který mají všechna vykreslení: pole jako YAML front matter, tatáž pole znovu jako tabulka a prompt nakonec pod ## PROMPT, dekódovaný a v ohrazeném bloku.

---
schema: 'majordomus.capture/v1'
ts: '2026-09-05T12:48:55Z'
provider: 'claude-code'
event: 'UserPromptSubmit'
id: 'bb6f8c96'
session: '5b13785c-728f'
source: 'user'
cwd: '~/src/your-repo'
repository: '~/src/your-repo'
branch: 'feature/prompt-capture-markdown'
head: 'a0ccbc9e25d6ec7f6bb754f3515affb9b1cc4014'
record: '20260905124855-make-it-a-file-per-prompt.json'
---

# Prompt — 2026-09-05 12:48:55 UTC

| | |
|---|---|
| **Started** | `2026-09-05T12:48:55Z` |
| **Provider** | `claude-code` · `UserPromptSubmit` |
| **Session** | `5b13785c-728f` |
| **Prompt** | `bb6f8c96` |
| **Source** | `user` |
| **Repository** | `~/src/your-repo` |
| **Branch** | `feature/prompt-capture-markdown` · `a0ccbc9` |
| **Directory** | `~/src/your-repo` |
| **Schema** | `majordomus.capture/v1` · `share/schemas/majordomus/capture/capture.v1.proto` |
| **Record** | `20260905124855-make-it-a-file-per-prompt.json` |

## PROMPT

```
make it a file per prompt
```

Pole se objevují dvakrát, protože oba čtenáři jsou různí: front matter je to, co parsuje nástroj, tabulka je to, co čte člověk, a jedna funkce zapisuje obojí z jednoho záznamu jedním průchodem, takže se nemohou rozejít. Řádek se vynechá, místo aby se vypsal jako null, protože řádek s hodnotou null říká člověku méně než chybějící řádek.

Identifikátor schématu je cesta k souboru, který jej popisuje. <namespace>.<name>/<version> je share/schemas/<vendor>/<name>/<name>.v<n>.proto, takže majordomus.capture/v1 se rozliší na share/schemas/majordomus/capture/capture.v1.proto bez registru uprostřed — čtenář, který drží záznam, drží i způsob, jak jej číst, a není nic, co by se mohlo rozejít se záznamy, které údajně popisuje. Ten soubor je protobuf použitý jako jazyk schématu, ne jako formát na drátě: nic tyto zprávy neserializuje a je to jediné místo, kde jsou definována pole záznamu a sekce dokumentu, takže se obě projekce nemohou rozejít. majordomus update jej instaluje; doctor selže, pokud identifikátor jmenuje soubor, který tam není.

Některá pole hlavičky chybí a chybějící znamená nepozorováno. Hook běží dřív, než se zavolá model, takže když se záznam zapisuje, tah ještě neproběhl: finished_at, duration_ms, model, effort a tokens z něj zjistit nelze a vyhovující zapisovatel je vynechá, místo aby zapsal null — chybějící pole říká „nepozorováno“, null by tvrdilo, že bylo pozorováno, že je to nic. Vykreslovač řádek vynechá, místo aby vypsal zástupný text, takže záznam zapsaný hookem ukazuje Started a žádné Finished. Jakmile je doplní něco, co tah pozorovat umí, řádky se objeví beze změny struktury. Totéž platí pro sekce ## CONTEXT a ## OUTPUT, které schéma deklaruje a vykreslovač je vypisuje právě tehdy, když je záznam obsahuje pole, na kterém stojí.

## OUTPUT je polovina výměny patřící modelu, kterou záznam promptu nesmí nést — doktrína selže záznam, který jmenuje odpověď nebo přepis. Vyhovující zapisovač ji uloží do samostatného záznamu po tahu, jehož meta.parent jmenuje záznam promptu, a vykreslení je spojí.

Ohrada se přizpůsobuje promptu, nikdy není pevně tři zpětné apostrofy. Prompty citují kód a prompt, který otevře vlastní ohradu, by blok ukončil předčasně a zbytek by vylil do dokumentu; renderer změří nejdelší řadu zpětných apostrofů uvnitř a otevře o jeden delší, takže se prompt cituje doslovně, aniž by se kdokoli dotkl jediného jeho bajtu.

Směr mezi nimi činí dvojici bezpečnou k vynucování: vykreslení lze znovu sestavit ze záznamu, ale záznam nelze nikdy znovu sestavit z vykreslení. Proto doctor selže u záznamu bez vykreslení a jako nápravu jmenuje capture render, a selže u vykreslení bez záznamu, aniž by nabídlo nápravu, protože žádná neexistuje — ta polovina se hlásí, aby ji člověk smazal. capture render projde archiv, zapíše chybějící vykreslení, nedotkne se ničeho, co už jedno má, a nikdy nezapíše záznam; --force přepíše všechna, což je pro změnu samotného vykreslení, ne pro cokoli, co dělá hook.

Záznam je po zápisu neměnný, dva hooky se nemohou prokládat uvnitř jednoho souboru, název se řadí chronologicky a výpis adresáře je už čitelnou historií. Slug je nápověda a nikdy věrné vykreslení — escapy a ne-ASCII se zhroutí do pomlček a samotný prompt je uvnitř souboru, aniž by se s ním cokoli dělo. Záznamy zůstávají idempotentní podle identity promptu poskytovatele, což je pole, ne název: hook doručený dvakrát během dne zapíše jednou.

Ne každý payload je prompt a pole, v němž dorazí, není pevné. Událost není tím, co naznačuje její název: Claude Code spouští UserPromptSubmit i pro zprávy, které vkládá do tahu — dokončenou úlohu na pozadí, systémovou připomínku — a to nejsou prompty člověka. Payload, jehož deklarovaný původ není člověk nebo jehož text začíná značkou, kterou poskytovatel vkládá, se přeskočí: žádný záznam a žádný řádek logu, protože přeskočení je běžný provoz, ne selhání. Payload, který o svém původu nic neříká, se zachytí, protože ztráta skutečného promptu je horší z obou chyb.

Klíče payloadu jsou kandidáti, a ne jedno předpokládané jméno — prompt se čte z prompt, pak prompt_text, pak text. Pole payloadu je soukromý tvar poskytovatele: verzuje se podle jeho rozvrhu, přejmenování není pro něj zásadní změnou a zachycení postavené na jednom předpokládaném jménu ztratí každý prompt v den, kdy se přesune. Když není přítomen žádný z kandidátů, nic se nezapíše a log jmenuje klíče, které tam byly, takže se další jméno přečte ze skutečného payloadu, místo aby se hádalo.

Archiv jsou důkazy, ne znalosti. Žije v ignorované polovině vrstvy, nic ho nenačítá do kontextu, žádný příkaz z něj nevyhledává a doctor selže, pokud je cokoli pod ním sledováno Gitem. Zapisovač vydává uzavřenou množinu polí, takže polovina výměny patřící modelu jím přijít nemůže.

Záznam nese epizodu, do které patří. Dříve nesl jen vlastní identifikátor relace poskytovatele a nic víc a převést jej na kanonické id epizody vyžadovalo state/sessions-open/<provider session>.yaml — soubor, který uzavření epizody maže. Takže ve chvíli, kdy epizoda skončila, nic nemohlo říct, do které epizody její prompty patřily. Změřeno v tomto repozitáři 2026-09-11: 974 záznamů, 80 relací poskytovatelů, 713 promptů bez přeživší cesty k epizodě.

Každý záznam nyní nese episode, episode_link, repository_id a worktree_id, zapsané, dokud mapování ještě existuje, s identitou, kterou počítá zbytek nástroje. episode_link říká, jak byla epizoda určena, a právě v tom rozdílu je smysl:

hodnotaco to znamená
openepizoda byla otevřená pod touto relací poskytovatele, když dorazil prompt — pozorování
session-contextpropojeno dodatečně, ze zmrazeného pracovního kontextu, který jmenuje relaci poskytovatele
ledgerpropojeno dodatečně, protože okno přesně jedné uzavřené epizody mohlo obsahovat prompty té relace poskytovatele
orphanpod touto relací poskytovatele nebyla otevřená žádná epizoda; žádná nebyla vymyšlena
unlinked-legacynapsáno dřív, než záznamy nesly epizodu, a nic nepřežilo, co by je propojilo

orphan a unlinked-legacy nenesou žádnou epizodu. Nic nepřipojuje prompt k epizodě nejbližší v čase: prompt přiřazený špatné epizodě je horší než ten, který není přiřazen žádné, protože druhý zjevně chybí, zatímco první tiše lže.

capture reconcile použije tyto tři druhy důkazů, v tomto pořadí, na archiv zapsaný dřív, než pole existovalo, a --dry-run řekne, co by udělalo. Odmítne ve chvíli, kdy by odpovědí mohly být dvě epizody, takže repozitář, jehož důkazy zmizely, drží počet nepropojených záznamů místo vymyšleného přiřazení — a doctor tento počet hlásí, místo aby ho zaokrouhlil na nulu.

Přihlašovací údaje se nikdy nedostanou na disk. Lidé vkládají klíče do promptů a záznam je jediný soubor, jehož obsah nikdo před zápisem neprověřil. Známé tvary přihlašovacích údajů — API klíče providerů, tokeny GitHubu, id klíčů AWS, tokeny Slacku a Stripe, hlavičky PEM, bearer tokeny a přiřazení dlouhé neprůhledné hodnoty něčemu, co se jmenuje klíč, token nebo heslo — se nahrazují cestou do záznamu, dříve než bajty jsou kdekoli jinde než v proměnné. Nic dále po proudu originál nevidí: ani záznam, ani jedno z vykreslení, ani název souboru, ani log, který vypisuje názvy klíčů payloadu a nikdy hodnotu. Co bylo nahrazeno, je zaznamenáno v redacted, takže „nic nebylo nalezeno“ a „tajemství je pryč“ jsou tvrzení, která čtenář dokáže rozlišit.

Archiv má režim 0600, včetně adresáře. Po celý svůj život měl 0644, čitelný každým účtem na stroji — včetně názvů souborů, které jsou začátky promptů. capture render, opravný příkaz archivu, nastaví režim nad celým adresářem při každém spuštění.

Retence bere tělo a ponechává záznam. Staré pravidlo znělo, že se nikdy nesmí nic odstranit, s argumentem, že prompt se z ničeho neodvozuje a žádný jiný soubor ho nerekonstruuje. Ten argument se týká záznamu — že se stalo, kdy, v které epizodě, pod kterou hlavou — a uplatňoval se na bajty textu, což je část, která nese přihlašovací údaje a jejíž riziko s hodnotou neklesá. Archiv dosáhl bez omezení 20 MB.

Politika proto deklaruje prompts.retention_max_days a prompts.retention_max_bytes a majordomus capture prune je aplikuje: nejdřív stáří, pak velikost, od nejstaršího těla. Proříznutý záznam si ponechává každé pole, včetně původu odkazu na svou epizodu, a získává náhrobek říkající, kdy tělo odešlo, jak bylo dlouhé a jaký měl otisk. Záznam se stále nikdy nemaže, doctor hlásí archiv nad kteroukoli mezí a nic neprořezává jako vedlejší efekt hooku, který je měl uchovávat.

capture status hlásí každého poskytovatele, kterého distribuce deklaruje, v obou aspektech, se dvěma fakty na řádek a nikdy jedním slovem za oba. Schopnost je dodavatelova, čtená z share/providers.yaml, kde každá buňka nese citaci, z níž byla ověřena; stav je stav tohoto checkoutu, vypočtený ze stromu.

stateco je pravda
unsupportedposkytovatel žádnou takovou událost vůbec nemá a deklarace říká, kde to bylo ověřeno. Nic, co tento nástroj umí, to nezmění.
unadaptedprovider událost dokumentuje a tato distribuce pro ni nedodává žádný adaptér. Mezera v nástroji, pojmenovaná jako taková.
unconfiguredadaptér existuje, ale tento repozitář jej nezapojuje
namedkonfigurace deklaruje hook, ale ne shim, který tento nástroj zapsal
wiredshim je na místě a spustitelný, ale payload jím prošel bez záznamu
verifiedsyntetický payload prohnaný shimem vytvořil záznam a jeho vykreslení

Repozitář se k tomu zavazuje deklarací položky enforcement s wired_by: provider-hook:<provider>; doctor pak selže, pokud stav není verified, a protože doctor běží na pre-commit, háček, který přestane zachytávat, zastaví commit.

Do ADR 0103 tento odstavec říkal něco nepravdivého a stojí za to říct co. Zněl: „Dnes má adaptér jen Claude Code; každý další provider, kterého distribuce deklaruje, je hlášen jako unsupported, ne předpokládán.“ Neplatila ani jedna polovina. Ostatní nebyli hlášeni jako unsupported — nebyli hlášeni vůbec, protože smyčka procházela providery s řádkem ve vnitřní shellové tabulce a ta měla jeden řádek; stav byl nedosažitelný pro každého providera, kterého šlo jmenovat. A unsupported přestalo být správným slovem pro dva z nich: k 2026-09-11 Codex CLI i Gemini CLI vydaly SessionStart, SessionEnd, událost před kompakcí a hook promptu spouštěný před modelem. Jsou unadapted — tato distribuce nedodává žádný adaptér — a citace pro všechno je v deklaraci.

Claude Code má stále jediný adaptér háčků. Orchestrátor jako bb žádný nemá záměrně: nepředává příkazu žádný prompt před modelem a agent, kterého spouští, si ponechává vlastní háčky, takže vlákno Claude Code pod bb je zachyceno jako claude-code (ADR 0024). agents je třída nástrojů, ne produkt, a nespouští vůbec nic. Žádný jiný povrch — web, desktopová aplikace, jiný stroj — odsud pozorovat nelze.

capture session — hranice epizody

Stejný argument, použitý na druhou věc, kterou pracovník nedokáže udělat sám: říct, kde začalo a skončilo jeho vlastní sezení. Model, kterému se řekne, aby otevřel relaci, ji otevře, když si vzpomene, což nikdy není ta epizoda, na které záleželo — ta, která skončila pádem, kompakcí nebo tím, že někdo zavřel okno. Poskytovatel vyvolá událost na obou okrajích bez ohledu na to, zda je model v pozici, aby si jí všiml, takže SessionStart epizodu otevře a SessionEnd ji uzavře do sdíleného záznamu pod .ai/repo/sessions/ (ADR 0015).

Oba směry jsou idempotentní, protože události jsou. SessionStart se spustí znovu při obnovení i při kompresi, takže start předá --if-open keep a otevřená epizoda se zachová místo nahrazení; SessionEnd se spustí, ať se cokoli otevřelo, nebo ne, takže uzavření předá --if-none ignore a nezapíše nic, když není co zavřít. O výsledku rozhoduje důvod události: ten, který adaptér uvádí jako záměrný, zavře epizodu jako closed, a cokoli jiného — pád, jméno, které tabulka neviděla — ji zavře jako interrupted, protože označit useknutou epizodu za úplnou je z těch dvou chyb horší.

Uzavření odvodí, co se epizoda naučila, mezi checkpointem a předávkou. session close spustí majordomus knowledge derive poté, co je záznam relace publikován a před připojením session.closed, kdykoli session.knowledge_on_end není false, takže po každém uzavření — adaptéru, člověka, jakéhokoli budoucího volajícího — následuje jeho řádek knowledge.derived; adaptér zhušťování volá deriver sám pod session.knowledge_on_compact, nezávisle na checkpoint_on_compact. Ani jeden se neptá, zda je úloha aktivní. Odvození, které selže, se hlásí jako provider.event.failed s posledním řádkem derivera jako důvodem, epizoda se přesto uzavře a hook skončí s 0; vypnutý přepínač se oznámí na stderr a nic neodvodí. KNOWLEDGE.md uvádí, co se z čeho odvozuje.

Žádný z hooků nezapisuje na standardní výstup. Claude Code přidává výstup hooku SessionStart do kontextu modelu a nic pod lokální polovinou vrstvy se nesmí načítat do kontextu implicitně. Diagnostika jde na stderr a capture session nikdy nekončí kódem 2 z důvodu, z něhož nikdy nekončí capture prompt.

Otevření zmrazí kontext, který dostalo. session start zapíše .ai/local/session-contexts/<stamp>--<session-id>.md: front matter nesoucí schema: session-context/v1, identitu epizody, providera a vlastní id relace providera — stejný řetězec, který nesou záznamy promptů — a pak výstup builderu kontextu doslova. session close připojí sekci ## Close jmenující výsledek a záznam, který zapsal. Ten soubor je úvodní snapshot, důkaz, co bylo workeru řečeno, a majordomus session context --path vypíše jeho cestu. majordomus session context samo skládá živý pracovní kontext při každém čtení: identitu epizody, git nyní proti gitu při otevření a checkpointy, rozhodnutí a otázky, které epizoda zaznamenala, čtené z řádků ledgeru označených jejím vlastním id relace. Nic ho necachuje, takže nemůže být zastaralý; --json ho nese vedle štítku čerstvosti snapshotu.

Toto úložiště je lokální a lokální zůstává. Uvádí tento stroj a je snímkem projekce — jeho opětovné rozlišení později dá jiný dokument — takže se nikdy nepublikuje, neindexuje ani samo nenačítá do kontextu. Není to ani přepis: odvozená polovina je výstup builderu, napsaná polovina práci shrnuje a klíč front matteru pojmenovávající seznam zpráv, dokončení nebo odpověď modelu je blokující selhání.

Zapojení je deklarováno jako wired_by: provider-hook:<provider>:session a doctor je dokazuje stejně, s jednou věcí vynechanou: syntetický payload se prožene koncovým shimem se zakázanou mutací, protože uzavření cizí otevřené epizody není cena, kterou smí diagnostika účtovat. Vše ostatní na cestě běží přesně jako při skutečné události.

$ majordomus capture install
INFO  capture  .claude/hooks/majordomus-capture  written and made executable
INFO  capture  .claude/hooks/majordomus-session-start  written and made executable
INFO  capture  .claude/hooks/majordomus-session-end  written and made executable
INFO  capture  .claude/settings.json  written with the UserPromptSubmit, SessionStart, SessionEnd hook(s)
$ majordomus capture status
agents                 unsupported  none        not a product but a class of tools defined by the file they read …
bb:session             unsupported  none        no conversation-lifecycle hook is published. Searched 2026-09-11: …
claude-code            verified     hook        .claude/hooks/majordomus-capture is wired, and a synthetic payload through it produced one record and its rendering
claude-code:session    verified     hooks       .claude/hooks/majordomus-session-start, …-end, …-compact are wired, and a synthetic payload through the end shim reached the command
codex:session          unadapted    hooks       the provider documents session events and this distribution ships no adapter for them — https://learn.chatgpt.com/docs/hooks …
generic:session        verified     connection  bin/majordomus-mcp is in place and a synthetic attach reached the episode boundary …
$ majordomus capture render
0 rendering(s) written into .ai/local/prompts

Najít trvalé záznamy, aniž by se četly všechny. Jen pro čtení.

Doslovné, nerozlišující velikost písmen vyhledávání pevného řetězce napříč předávkami, checkpointy, rozhodnutími, otázkami, aktivy promptů a ledgerem, v tomto pořadí — v pořadí autority, takže nejspolehlivější důkaz je první. --kind jej omezuje a lze jej opakovat; --task zúží na jednu úlohu; --limit omezí každý druh.

Záměrně to není index ani embedding. Korpus je hrstka souborů Markdown a jeden JSONL; sken je rychlejší než problém se zastaráváním, který by index přinesl, a „průhledné“ je zde cennější než „chytré“. Kód 0 s nálezy, 12 bez.

$ majordomus search "callback" --kind decision --kind checkpoint
checkpoint  .ai/local/state/checkpoints/20260903T194500Z--main--3f2a9c1--8c1d0e4a.md:12  Cause is in callback normalisation, not in the comparison.
decision    .ai/local/state/decisions.md:31  ## 2026-09-03 — Normalise the callback URI before comparing state
search: 2 match(es)

majordomus knowledge

Kompilátor nad tím, co tento repozitář už uvádí, a zapisovatel toho, co se epizoda naučila. sources, nodes, edges, search, show, related, candidates a check jsou jen pro čtení; derive, promote a reject zapisují záznamy znalostí a řádky knihy a nic jiného.

Není to wiki, databáze, paměťová služba ani druhé místo, kam si věci zapisovat. Každý zdroj, který čte, je soubor, který už někdo udržuje, vše, co vytváří, je odvozené a regenerovatelné a nic z toho nepřebíjí soubor, z něhož to pochází. Výsledek je ukazatel na kanonické znalosti, nikdy přepsaná odpověď.

knowledge není search. search je doslovné prohledání trvalých provozních záznamů — předávek, checkpointů, rozhodnutí, otázek, promptů, knihy — bez indexu, protože tento korpus je hrstka souborů. knowledge pokrývá kanonické artefakty repozitáře: politiku, profily, prompty, smlouvy milníků a úkolů, matici tvrzení a dokumenty. Dva korpusy, dvě smlouvy a žádná nemění tu druhou.

  • sources [--scope shared|operational|all] vypíše kurátorované třídy zdrojů a soubory, které každá objevuje, s třídou, rozsahem, druhem, hashem obsahu a cestou relativní k repozitáři.

  • nodes [--scope ...] [--kind <k>] odvodí jeden uzel na kanonický objekt: jeho identitu, druh, zdroj, ze kterého pochází, a hash tohoto zdroje. Skončí s 10, když dva objekty nárokují jednu identitu.

  • edges [--scope ...] [--type <t>] odvozuje jednu hranu na každý uvedený vztah, se souborem a polem nebo řádkem, kde byl pozorován.

  • search <query> [--scope ...] vrací uzly, které dotaz odpovídá, v pořadí, které je pravidlem, ne skóre. Uzel se zařadí na první příčku, kterou dosáhne: id (dotaz je jeho kanonické id), title (dotaz je jeho titulek), title-prefix (jeho titulek začíná dotazem), path (jeho zdrojová cesta obsahuje dotaz), body (jeho zdrojový soubor obsahuje dotaz doslova); každé porovnání ignoruje velikost písmen. V rámci příčky je pořadí bajtové pořadí id. Každý výsledek uvádí příčku, na kterou byl zařazen (matched a rung v --json), takže důvod pořadí je v odpovědi. Uzly nenesou tagy, proto neexistuje příčka tagů.

  • show <id> [--scope ...] vypíše jeden uzel s jeho druhem, rozsahem, titulkem, cestou zdroje a hashem obsahu. Skončí s 12, když žádný uzel to id nemá.

  • related <id> [--scope ...] vypíše hrany z jednoho uzlu a do něj, každou s jejím typem a souborem a polem nebo řádkem, kde byla pozorována. Skončí s 12, když žádný uzel nemá toto id.

    Žádný ze tří nezapisuje a žádný nic nepřestavuje: není tu uložený index, který by mohl zastarat, takže každé volání zkompiluje to, co checkout v tu chvíli obsahuje, přesně jako nodes. Případ 3037 ověřuje žebřík, pole každého výsledku a že pracovní strom po všech třech vypadá stejně.

  • derive [--episode <id>] [--dry-run] [--json] odvozuje znalosti, které epizoda vytvořila, do kandidátních záznamů pod .ai/repo/knowledge/candidates/, z knihy a gitu a nikdy z konverzace: zaznamenané rozhodnutí se stane convention, vyřešená otázka fact, zablokovaná nebo selhaná úloha lesson, dokončení s ověřovacím příkazem fact. Otevřená epizoda, nebo ta pojmenovaná. Deterministické: stejná kniha, stejný git, stejné bajty a druhý běh hlásí každý záznam jako unchanged. --dry-run řekne, co by zapsal, a nedotkne se stromu ani knihy.

  • candidates [--json] vypíše každý záznam čekající na revizi — id, třídu, datum, větev epizody, ze které pochází, název — čtením adresáře, ne indexu, takže se vypíše i kandidát, kterého hook právě zapsal a nikdo ho nepřidal.

  • promote <id> [--class <c>] < evidence.md přesune kandidáta do curated/ jako verified, s důkazy ze standardního vstupu připojenými pod # Evidence a třídou, kterou zvolil člověk. Deriver nikdy nezapisuje verified; toto je akt, který ano.

  • reject <id> --reason "<why>" [--by <id>] označí kandidáta superseded na místě s důvodem pod # Rejected a pojmenuje záznam, který ho nahrazuje, když ho --by uvede.

  • check [--json] validuje každý záznam pod candidates/ a curated/: schéma, žádný neznámý klíč, jedinečná id rovná názvu souboru, původ vedle verified, každý odkaz se řeší, důvod vedle superseded, žádný kandidát nenárokující verified, žádná konverzace. Stejnou kontrolu spouštějí check a doctor.

Čtecí stranu téhož úložiště obsluhuje spustitelný soubor — bin/majordomus-cli knowledge candidates, knowledge record <id> a knowledge status, každá schopnost deklarovaná jednou a odpovídající stejně přes HTTP, MCP i příkazovou řádku (CAPABILITIES.md); status hlásí poslední derivaci, nejnovější uzavření a to, zda zapisovatel přestal běžet. KNOWLEDGE.md obsahuje tabulku derivací, oba okamžiky a pravidla.

Zapisuje: derive zapíše .ai/repo/knowledge/candidates/<id>.md, jeden na záznam, přes dočasný soubor ve stejném adresáři přejmenovaný přes soubor pojmenovaný id, nikdy neindexovaný, a vypíše written, unchanged nebo skipped na záznam se shrnutím na konci; připojí knowledge.derived do účetní knihy — epizodu, počty, cesty — i když nezapsal nic. promote zapíše .ai/repo/knowledge/curated/<id>.md stejným způsobem, odstraní kandidáta, připojí knowledge.promoted a nakonec vypíše cestu k vybranému záznamu. reject přepíše kandidáta na místě a připojí knowledge.rejected. Id záznamu je id epizody a digest důkazů, takže stejný fakt vždy skončí ve stejném souboru.

Nikdy nestaguje, necommituje ani neupravuje žádný jiný soubor. Nikdy nečte konverzaci, prompt ani předávku; nikdy nevolá model ani síť. Nikdy nezapisuje verified.

Odmítá (10) promote s prázdným stdin, se stdin nesoucím značku přepisu, na záznamu, jehož stav není candidate, nebo s třídou mimo výčet; reject na záznamu, jehož stav není candidate, nebo když --by nejmenuje žádný záznam; derive když se politika nedá zpracovat; check na jakémkoli selhávajícím záznamu. (12) promote nebo reject, když id nenese žádný kandidát. (2) reject bez neprázdného --reason. (13) zápis, který nedoběhl. derive bez epizody, z níž by se dalo odvozovat, to řekne a skončí s 0.

$ majordomus decision add "Tabs are refused in the parser" --why "two encodings of one thing"
$ majordomus knowledge derive
written .ai/repo/knowledge/candidates/s-20260912101500-7f1a-3b9c2e4d1a05.md
knowledge derive: 1 written, 0 unchanged, 0 skipped for episode s-20260912101500-7f1a
$ majordomus knowledge derive
unchanged .ai/repo/knowledge/candidates/s-20260912101500-7f1a-3b9c2e4d1a05.md
knowledge derive: 0 written, 1 unchanged, 0 skipped for episode s-20260912101500-7f1a
$ printf 'Verified by reading lib/yaml.sh.\n' | majordomus knowledge promote s-20260912101500-7f1a-3b9c2e4d1a05 --class constraint
.ai/repo/knowledge/curated/s-20260912101500-7f1a-3b9c2e4d1a05.md

Žádná hrana bez původu. Každá hrana pojmenovává, kde byl vztah uveden, a hrana, které chybí from, to, type nebo provenance, se odmítne a nevydá s prázdnou hodnotou — prázdný zdroj se čte jako „neznámý“ a nelze jej odlišit od zdroje, který nikdo nezaznamenal. Hrana, kterou nikdo nedokáže vysledovat k řádku, není fakt, je to dohad v šatech faktu, a je to dohad, u něhož člověk, který by si nejlépe všiml, že je špatně, to nikdy neuvidí.

Nic se neodvozuje z prózy. Repozitář své vztahy již uvádí explicitně, v polích, která někdo udržuje, a ta jsou zároveň zdarma i správná. Typy hran tvoří uzavřenou množinu; nedeklarovaný typ je chyba, a ne nové slovo slovníku:

typz → douvedeno v
part_ofúkol → milníkmilestone úkolu
depends_onissue → issue, milestone → milestonedepends_on záznamu
declaresmilestone → claimclaims milníku
specified_bytvrzení → dokumentdocs/CLAIMS.yaml's source
implemented_bytvrzení → implementacejeho implementation
tested_byclaim → test, doctrine → testjeho test
supportsdoktrína → tvrzeníclaims doktríny
referencesdokument → dokumentvložený odkaz v Markdownu

Odkazy jsou jediný zdroj hran, který není kurátorovaný, a čtou se konzervativně. Nejprve se zahodí oplocený kód, protože cesta uvnitř ukázky kódu je příkladem cesty, ne odkazem na ni. Cíl se schématem, cíl relativní k protokolu a absolutní cesta se přeskočí; fragment se ořízne, protože docs/CLI.md#session odkazuje na docs/CLI.md.

Závažnost rozlišuje, co vlastní Majordomus a co napsal autor. Deklarovaný vztah ukazující na soubor, který repozitář neobsahuje, je FAIL: je to porušený slib. Odkaz v dokumentu ukazující na nic je WARN: dokument může záměrně ukazovat mimo repozitář a samotný cíl tyto dva případy nerozliší. Vztah ukazující na skutečný soubor, který tento kompilátor nemodeluje jako jediný uzel — kontejner mnoha objektů nebo něco mimo vybranou množinu — je tichý, protože zpráva, která je záměrně obrovská, je zpráva, kterou lidé přestanou číst.

nodes a edges jsou dva pohledy na jedno odvození, takže vada v kterémkoli končí kódem 10 u obou. Nad rozbitým grafem neexistuje čistý výpis uzlů.

Identita uzlu nikdy není hash jeho obsahu. Je to vlastní kanonické id objektu, pokud ho má — claim:policy-parse, issue:I0801, milestone:M003, profile:debugging — a cesta v repozitáři, pokud ne, jako v document:docs/CONTINUITY.md. Hash říká, zda se něco změnilo; nikdy nemůže říct, čím něco je, protože pak by každá úprava smazala uzel a vytvořila cizince a každá reference na něj by mířila nikam, aniž by to cokoli řeklo. Hash jede s uzlem a měří se jím aktuálnost.

Důsledek je záměrný: úprava dokumentu zachovává jeho uzel a mění jeho hash; přejmenování je smazání a přidání a neruší žádný jiný uzel.

Druh vychází ze struktury, nikdy z prózy. Určuje ho třída zdroje, ve které byl soubor objeven, a pole, která soubor sám deklaruje. Nic nečte tělo dokumentu a nehledá slovo, které by naznačovalo typ: dokument, který pojednává o plánech rozvoje a milnících, je dokument, který o nich pojednává. Kde se nepoužije žádné pravidlo, je druh unknown a uzel se přesto vydá, s jedním nálezem pojmenovávajícím třídu — explicitní neznámo je informace, sebejistá chybná odpověď není.

Název se převezme, nebo se nechá prázdný, nikdy se nevymýšlí. Z vlastního title nebo description záznamu, nebo z prvního nadpisu první úrovně dokumentu. Dokument bez nadpisu nedostane žádný název, ne název souboru, protože název souboru nahrazující titul se čte jako fakt a je to odhad.

Dvě úložiště nemají vlastní pole id. Rozhodnutí je klíčováno svým názvem a otázka svým textem — v obou případech přesně tím, co zaznamenává kniha a na co odkazuje obálka relace, takže tři místa pojmenovávají totéž stejně a nic mezi nimi nemapuje. Zodpovězení otázky přepíše řádek, na kterém žije, a identita to přežije: připojená odpověď se odstraní, a jen když to, co zbude, končí datem otevření záznamu, takže otázka, jejíž vlastní text obsahuje pomlčku, zůstane celá a neřízne se naslepo.

Objevování řídí index repozitáře, ne procházení souborového systému. Procházení vrací výstup buildu, vendorované stromy a odpadky z editorů; vrací je v pořadí, které se mezi dvěma stroji liší; a může vrátit soubor, který nikdo nechtěl publikovat. Výpis sledovaných souborů místo toho dává pravdu repozitáře ve stabilním pořadí a nesledovaný soubor není nikdy zdrojem. Provozní záznamy se objevují ze stavového adresáře, který si Majordomus sám vlastní pod .ai/local/ a který se nikdy nesleduje — a odnikud jinud. Žádný skrytý adresář se neprohledává jen proto, že existuje.

Třídy zdrojů jsou deklarovány dvakrát, dvěma vlastníky: repozitář deklaruje své sdílené zdroje ve své AI vrstvě, .ai/repo/knowledge/sources.yaml, a nástroj dodává provozní třídy, záznamy, které zapisuje sám, v share/knowledge-sources.yaml. Rozsah třídy určuje to, který soubor ji deklaroval; ani jeden soubor nejmenuje druhý:

scopevýznam
sharedznalosti repozitáře; smí se promítnout na veřejný povrch
operationalvlastní pracovní záznamy tohoto checkoutu; nikdy součást sdílené projekce

Rozsah je vlastnost třídy, takže hranice publikace se rozhoduje na jednom místě, ne u každého producenta. Každý sledovaný pathspec nese prefix :(glob), pod kterým * nepřekračuje oddělovač adresářů. To není úprava pro pořádek: bez něj docs/*.md odpovídá i docs/claims/*.md, dvě třídy se tiše překrývají a jeden soubor se stává dvěma uzly. test/cases/64_knowledge_discovery.sh na této mutaci selže.

Jedno překrytí řeší soubor, ne třídy. README.md sekce odpovídá jak .ai/**/README.md, třídě context, tak vlastnímu pathspecu své sekce (.ai/repo/features/*.md, .ai/repo/rules/**/*.md a tak dále). Deklaruje kind: context, takže je to uzel context, jednou: druhá třída, která jej objeví, nepřidá nic. Index uplatňuje stejnou přednost v apps/majordomus-cli/src/discovery/mod.rs a test/cases/330_a_contract_is_the_kind_it_declares.sh ji prokazuje pro knowledge nodes. Kde žádný soubor druh nedeklaruje, zůstávají dvě třídy nad jednou cestou odmítnutím (twin v případu 73).

init deklaruje nejdřív třídu context, takže smlouvy nového repozitáře jsou od začátku objekty jeho indexu. Bez toho se context resolve, který čte soubory, a každá schopnost čtoucí index neshodly v tom, co se na cestu vztahuje; test/cases/130_context_compiler.sh bez této třídy selže.

Třída označená required, která nic nenajde, se hlásí jako WARN. Cenou za kurátorský seznam je, že na cestu lze zapomenout, a zapomenutá cesta se neliší od repozitáře, který takový soubor nemá, pokud to něco neřekne.

Zda soubor odpovídá schématu svého druhu, je verdikt indexu spustitelného souboru, ne čtenáře: knowledge nodes se indexu ptá, které soubory odmítl, vyřadí je a každé odmítnutí hlásí jako FAIL (test/cases/405_the_knowledge_reader_honours_the_schema.sh). Bez sestaveného spustitelného souboru nebo bez jq hlásí WARN, že schéma zůstalo nezkontrolováno; nikdy se nečte jako úspěch. MAJORDOMUS_KNOWLEDGE_SCHEMA=unasked říká, aby se neptal, a čtenář pak hlásí INFO, že se kontrola nevyžádala. To nastavuje zaznamenaný scénář případu užití: zda se index mohl zeptat, je fakt o nahrávajícím stroji a důkaz, který se musí na každém stroji číst stejně, jej nést nemůže (#713, test/cases/777_a_recorded_scenario_does_not_depend_on_the_recorder.sh).

$ majordomus knowledge sources --scope shared
policy      shared      policy     4f2a9c1d8b30  .ai/repo/policy.yaml
profile     shared      profile    a1b2c3d4e5f6  .ai/repo/profiles/debugging.yaml
...
issue       shared      issue      0e5a7b9c3f51  .ai/repo/project/issues/I0801.yaml
claims      shared      claim      7c9e1b3d5f70  docs/CLAIMS.yaml
document    shared      document   9b1e2d4f8c3a  docs/CONTINUITY.md
knowledge sources: 169 file(s) in scope shared

majordomus rules

Efektivní sada pravidel: každé aktivní pravidlo vendorované pod sekcí pravidel repozitáře plus každé aktivní pravidlo, které repozitář napsal, vyřešené jako graf závislostí. Jen pro čtení v každém podpříkazu kromě vendor update.

Pravidlo je soubor Markdown s YAML front matter. Jeho identitou je id a version z front matter, nikdy název souboru. docs/DOCTRINE.md popisuje formát, blok x-majordomus, který váže pravidlo na validátor, a co je dnes autoritativní.

$ majordomus rules list
majordomus.scope-integrity                 v1  blocking  vendor:majordomus enforced by check,finish,watch
majordomus.sessions-are-workers            v1  advisory  vendor:majordomus no validator; see the rule
project.english-only                       v1  blocking  project          proven by scripts/ci/english-only-check
project.clean-room                         v1  blocking  project          review-enforced: the rule is about the provenance of what was written, which no program in this tree can read
  • list [--json] vypíše účinnou sadu v pořadí řešení: identita, třída, původ (vendor:<name> nebo project) a způsob vynucování. Tři režimy, které popisuje DOCTRINE.md: enforced by <commands> pro pravidlo, které spouští dispatcher, proven by <paths> pro pravidlo, které drží kontrola nebo případ, a review-enforced: <reason> pro pravidlo, které nese reviewed_because vysvětlující, proč ho žádný program nevyjádří. Pravidlo bez bloku x-majordomus říká no validator; see the rule, místo aby se skrylo: je normativní pro každého, kdo ho čte, a nic ho strojově nekontroluje.

    Tento příkaz odpovídá na to, co pravidla jsou. Co každé z nich prokazuje — zda případ, který jmenuje, ve stromu stále je, zda jej některý runner řídí, zda jej kdy někdo spustil a zda je ten běh starší než to, čeho se týká — je jiná otázka a odpovídá na ni spustitelný soubor: majordomus-cli rules report, rules show <id>, rules proves <test>.

  • show <id> vypíše jedno pravidlo, front matter a tělo, s cestou relativní k repozitáři, z níž bylo přečteno, jako prvním řádkem. Id mimo efektivní sadu skončí kódem 12.

  • vendor status porovná vendorovaný základ s balíčkem, který dodává běžící spustitelný soubor. Vypíše obě revize, pak integritu manifestu vendorované kopie a nakonec, zda jsou oba balíčky stejné.

  • vendor diff je prověřitelný rozdíl mezi oběma, jako unified diff obou adresářů. Skončí s kódem 0, ať se liší, nebo ne; vendor status nese výstupní kód.

  • vendor update [--force] nahradí vendorovaný základ balíčkem spustitelného souboru. Zápis je atomický: nový balíček se připraví vedle cíle a prohodí se. Nikdy se nedotkne rules/project/.

Řešení selhává uzavřeně. Chybějící závislost, závislost na zastaralém pravidlu, cyklus, jedno id@version nárokované dvěma soubory, projektové pravidlo ve jmenném prostoru dodavatele, poškozený nebo neúplný front matter, neznámý klíč front matteru nebo blok x-majordomus, který nejmenuje žádný validátor, žádný vynucující příkaz nebo žádný test — každé z toho zastaví list, show i každý příkaz, který čte danou sadu, s kódem 10 a důvodem. Nic se neaplikuje částečně. Pořadí je deterministické: dva běhy se shodnou a každá závislost je uvedena před pravidlem, které na ní závisí.

Vendorovaná kopie repozitáře je autoritativní. Novější spustitelný soubor hlásí novější základ přes vendor status a vendor diff; nikdy žádný neaplikuje. update, doctor a check nechávají vendorovaný adresář na pokoji. Základ se mění, jen když se o to požádá vendor update.

Ruční úprava pod vendor/ je zjištěna. Manifest balíku uvádí každý soubor pravidel s jeho hashem. Soubor, jehož hash už nesedí, uvedený soubor, který chybí, nebo soubor vedle manifestu, který v něm není uveden, hlásí vendor status a odmítá vendor update, dokud --force.

ukončenívýznam
0sada se vyřeší; vendorovaný základ je aktuální
2neznámý podpříkaz nebo volba; show bez id
10sada se nerozliší, nebo vendorovaná kopie neodpovídá svému manifestu
11vendor status: spustitelný soubor dodává jiný balíček než ten, který je vendorovaný
12žádné pravidlo s tímto id; zatím nic vendorováno; v tomto rozvržení není sekce pravidel
13spustitelný soubor nedodává žádný standardní balíček pravidel: rozbitá instalace, ne chyba repozitáře
15vendor update odmítnuto kvůli ručně upravenému vendor adresáři (--force přepíše)

test/cases/67_rule_dag.sh dokazuje každé odmítnutí mutací a dokazuje, že balíček novější distribuce se neuplatní, dokud o to nikdo nepožádá, že vendor update ponechá rules/project/ bajt po bajtu, jaké bylo, a že rozřešené pořadí je napříč běhy stejné.

majordomus finish

Vyhodnotit kontrakt dokončení. Odmítnout, pokud není splněn.

Čte: politiku, profil, current.yaml, git, knihu. Zapisuje: při úspěchu výsledek current.yaml nastavený na zadanou hodnotu, jeden řádek knihy task.finished nesoucí vyhodnocený kontrolní seznam a výsledek ověření a kopii --note pod state/completed/<id>.md, pokud je zadána.

Argumenty: --outcome completed|partial|blocked|no_match|failed povinný. --verify-command "<cmd>" spustí vlastní ověření projektu v kořeni repozitáře a zaznamená jeho návratový kód, dobu trvání, příkaz a id git stromu pracovního stromu, nad kterým běželo. Strom se čte před během i po něm: pokud se změnil, zatímco příkaz běžel — druhý worker ve stejném checkoutu, člověk ukládající soubor, příkaz přepisující sledované soubory — běh nepopisuje ani jeden stav a řádek selže; spusťte jej znovu, až se strom ustálí. --note <file> dodá poznámku o dokončení; jinak se použije nejnovější předávka, která tuto úlohu jmenuje. --check vyhodnotí rozsah a stav bez zápisu a skončí s 0, když není aktivní žádná úloha nebo je úloha už dokončená, takže pre-push hook nikdy nezablokuje repozitář, ve kterém není co vynucovat.

Požadavky profilu se vyhodnocují i pro completed: regression_test_required projde, když se dotčená cesta jeví jako test (test/, spec/, _test., .spec.); decision_record_required projde, když decisions.md obsahuje Task: <id>. Kontrola regrese je záměrně hrubá a říká to ve své zprávě.

Kontrakt pro completed:

scope respected        touched files within claimed paths
verification ran       --verify-command exited 0, recorded
state updated          current.yaml is at HEAD or advanced, not diverged
no open blockers       open-questions.md has no unresolved entry for this task
note present           newest handover or completion note has required sections

Každý řádek smlouvy se vyhodnotí a vypíše, ať projde, nebo ne, aby odmítnutí řeklo přesně, co chybí. partial a blocked vyžadují poznámku s # Next Action; no_match a failed vyžadují # Reason; všechny čtyři přeskakují řádek ověření a blocked přeskakuje řádek blokátorů. Když selže kterýkoli řádek, nic se nezapíše. Dokončení již dokončené úlohy se odmítne (15).

$ majordomus finish --outcome completed --verify-command "make test"
OK   scope         12 files, all within lib/auth
OK   verification  make test — exit 0, 41s
OK   state         exact (head 9b1e2d4)
FAIL blockers      open-questions.md: "token refresh window — needs product decision" unresolved  [reproduce: grep -n 'unresolved' .ai/local/state/open-questions.md]
OK   note          handover 20260903T201455Z--main--9b1e2d4--c0ffee.md
finish: refused, 1 unmet
blocking doctrines:
- majordomus.blocker-resolution
$ echo $?
10

Smlouva není seznam uvnitř finish. Je to sada doktrín, jejichž enforced_by jmenuje finish, vybraná verification.finish_requires tohoto repozitáře. Požadavek v politice, který nedefinuje žádná doktrína, se hlásí a odmítá, místo aby se ignoroval. Viz DOCTRINE.md.


majordomus doctrine

Ohlásit, která pravidla se zde vynucují, čím a zda jsou zapojena. Pouze pro čtení.

Čte: efektivní sadu pravidel repozitáře (.ai/repo/rules/vendor/majordomus/ a .ai/repo/rules/project/, vyřešenou tak, jak ji řeší majordomus rules list), lib/, docs/CLAIMS.yaml. Zapisuje: nic.

majordomus doctrine [status]     derived counts
majordomus doctrine list         id, class, validator, enforcing commands
majordomus doctrine show <id>    the full record for one doctrine, and the rule file it lives in

Behaviour:

  • status vypíše, kolik doktrín je deklarováno, kolik jich blokuje, kolik je advisory, kolik jmenuje neexistující validátor a kolik jmenuje neexistující testovací soubor. Každé číslo se odvozuje na místě; žádné se neukládá.
  • list vypíše jeden řádek na doktrínu.
  • show <id> vypíše záznam, včetně toho, která tvrzení podpírá, testů, které ho dokazují, pravidel, na kterých závisí, souboru pravidel, v němž žije, a souboru, který definuje jeho validátor. Neznámé id skončí kódem 12. Id doktríny je id jejího pravidla, majordomus.<name> pro základní.
  • Sada pravidel, která se nevyřeší, skončí s 10 a důvodem; nic se nevypíše částečně.
  • Skončí s kódem 0, když nechybí žádný validátor ani testovací soubor, jinak s 10. Zda vynucování skutečně funguje, je silnější otázka a odpovídá na ni majordomus doctor.

majordomus plan

Číst kanonický projektový model a provést jeden úkol jeho životním cyklem. Milník je spustitelná specifikace výsledku; úkol je ohraničený kontrakt provedení; graf závislostí mezi úkoly rozhoduje, co se smí provést dál. Sémantiku viz docs/PLANNING.md.

Čte: .ai/repo/project/project.yaml, .ai/repo/project/milestones/*.yaml, .ai/repo/project/issues/*.yaml, share/allow/{project,milestone,issue}.txt. Zapisuje: jedno pole životního cyklu v jednom souboru úkolu a jednu událost knihy — jen start, verify, evidence a done. Každý další podpříkaz nezapisuje nic.

majordomus plan validate         schemas, references, the DAG, status consistency
majordomus plan status           milestone progress and the next executable issue
majordomus plan list             one line per issue: id, status, wave, milestone, title
majordomus plan show <id>        the full record of one milestone or issue
majordomus plan ready            issues whose dependencies are all satisfied
majordomus plan blocked          issues waiting on a dependency, and on which one
majordomus plan waves            topological execution waves, derived from the graph
majordomus plan graph            the dependency DAG as Mermaid
majordomus plan next             the one issue a worker should take now
majordomus plan body <id>        the provider-neutral projection body for one record
majordomus plan start <id>       record that execution began
majordomus plan verify <id>      record that implementation is complete, evidence pending
majordomus plan evidence <id>    attach one piece of evidence
majordomus plan done <id>        record completion

Volby: --json u čtecích podpříkazů, které povrch spotřebovává; --milestone <id> k omezení list, ready, blocked, waves a graph; --covers, --type, --command, --result a --artifact u evidence.

Behaviour:

  • Žádný stav se nikde neukládá. BLOCKED, READY, ACTIVE, VERIFY, DONE a CANCELLED se odvozují z toho, co úkol zaznamenává o sobě, a ze stavu jeho závislostí, pomocí lib/project.awk. Zápis pole status: je neznámý klíč.
  • validate hlásí neznámé klíče, neznámé a vlastní závislosti, duplicitní id, cykly, úkoly provedené před svou závislostí a úkoly bez kritérií přijetí. Skončí s 10, když je jakýkoli nález selhání, a s 0, když zbývají jen varování.
  • start odmítne (15) úkol, který není READY, a uvede, na co čeká. verify odmítne úkol, který není ACTIVE.
  • evidence odmítne (15) token, který úkol nedeklaruje, a odmítne (2) bez --command nebo --artifact: vyprávění není důkaz. Připojí se k souboru úkolu s commitem a časovým razítkem.
  • done odmítá (10), dokud je jakýkoli deklarovaný token důkazu nepokrytý, a odmítá (15), dokud závislost není DONE. Každý zapisující podpříkaz vypíše další připravený úkol po přepočtu grafu.
  • Končí kódem 12, když pojmenovaný záznam neexistuje nebo když repozitář nemá vůbec žádný kanonický projektový model — model je dobrovolný a doctor ho přeskočí, místo aby tam, kde chybí, selhal.

Stejný model promítá na GitHub scripts/github-sync a na web scripts/generate-site-data. Žádný z nich neodvozuje stav znovu; oba čtou tento engine.

majordomus intent

Co se musí stát pravdou nad milníky a zda smí práce k tomu postupovat. Spustitelný soubor odpovídá; každý podpříkaz je jedna schopnost modulu intents, takže HTTP a MCP odpovídají toutéž hodnotou. Sémantiku viz docs/PLANNING.md; generovaná reference vypisuje každou volbu.

Čte: .ai/repo/project/intents/*.yaml, plán, .ai/repo/project/gaps/*.yaml, .ai/repo/project/critiques/*.yaml, knihu důkazů. Zapisuje: nic.

majordomus intent list                     every intent with its derived stage
majordomus intent show <id>                one intent in full
majordomus intent validate                 every finding over the intents, coverage included
majordomus intent coverage                 which work carries which criterion, and why
majordomus intent preflight --issue <id>   may the work on an issue proceed, held to what
majordomus intent preflight --path <p>     the same for the open issues covering a path
majordomus intent binding --issue <id>     what a task naming that issue is bound to, with its pins
majordomus intent realization              which work realises which intent, and whether reality agrees
majordomus intent explain <id>             why an intent stands where it stands

preflight chování: jediný join, který relace nebo přechod spotřebuje před začátkem práce.

  • Úkol se sleduje přes odkazy <intent>#<criterion>, které deklaruje v serves, nikdy jen přes svůj milník, a každý odkaz se posuzuje podle pokrytí, z něhož validate vychází.
  • Verdikt je serves (exit 0), když platí každý odkaz a plán každého obsluhovaného záměru prošel kritikou bez blokujícího nálezu open; maintenance (exit 0), když práce leží pod milníky, které nejmenuje žádný živý záměr, a nic neobsluhuje, přesně tam, kde to validate dovoluje; refused (exit 10) jinak.
  • Každé odmítnutí pojmenovává svůj úkol a příčinu: unknown_issue, no_issue_covers_paths, issue_serves_nothing (jeho milník realizuje záměr a on neslouží žádnému z jeho kritérií), serves_another_intent (kritérium záměru, které nepojmenovává svůj milník), serves_unknown_criterion (záměr nebo kritérium, které neexistuje, nebo chybně utvořená reference), intent_not_critiqued, open_blocking_finding.
  • S --path se posoudí a vypíše každý otevřený úkol, jehož rozsah pokrývá cestu, s vlastním verdiktem, a odpovědí je ten nejhorší z nich: jeden obsloužený úkol nikdy neručí za jiný.
  • Odpověď nese záměry, kterým práce slouží, a žádné jiné: každý s jeho prohlášením, splněnými kritérii s živým stavem jejich důkazů, invarianty, necíli, governance, jeho kritikou s dosud otevřenými blokujícími nálezy a jeho mezerou omezenou na splněná kritéria.

majordomus adr

Architektonická rozhodnutí repozitáře jako objekty: co se rozhodlo, proč, co to stálo a odkud záznam pochází. V sekci vrstvy adrs.

Rozhodnutí je soubor .ai/repo/adrs/<NNNN>-<slug>.md — YAML front matter splňující share/schemas/majordomus/adr/adr.v1.schema.json nad tělem nesoucím # Context, # Decision a # Consequences. Nic jej neregistruje: třída zdroje adr v .ai/repo/knowledge/sources.yaml jej objeví nad sledovaným stromem, což je táž deklarace, kterou indexuje spustitelný soubor v Rustu. Kontrakt viz SCHEMAS.md.

majordomus adr list [--status <status>] [--json]      every decision: id, status, date, title
majordomus adr show <id> [--json]                     the path, then the file as written
majordomus adr next [--json]                          the next free identity, and every source surveyed
majordomus adr propose "<title>" [--from <ref>]...    write a new decision, status proposed
                       [--tag <tag>]... [--supersedes <id>]
majordomus adr check [--json]                         validate every decision and every reference
majordomus adr affected [--base <ref>|--staged|--worktree] [--json]   the decisions a change set touches
  • list vypíše jeden řádek na každé objevené rozhodnutí, včetně neplatných; --status filtruje na jedno z proposed, accepted, superseded, rejected.

  • show vypíše cestu relativní k repozitáři na prvním řádku a pod ní soubor. Id, které není rozhodnutí, skončí kódem 12 a pojmenuje adr list.

  • next odpovídá, jaká je další volná identita, a říká, na co se při rozhodování podíval. Čtyři zdroje, z nichž každý hlásí, kolik identit si nárokoval: tento pracovní strom, každý sourozenecký worktree tak, jak je na disku (autorsky napsané a dosud necommitnuté je skutečný nárok), každý ref — větve, tagy, remote-tracking refy a HEAD každého propojeného worktree, v jednom git log --all --no-renames --diff-filter=A — a nástěnka peerů sdíleného serveru, která je jediným zdrojem, jenž ví o identitě, kterou se relace rozhodla vzít a nikam ji nezapsala. Zdroj, který nebylo možné oslovit, se hlásí jako nedostupný a nikdy se mlčky nevypouští ze jmenovatele. Alokace je monotónní: o jedna nad nejvyšší identitou, kterou kdy cokoli nárokovalo, nikdy do mezery. Mezera znamená, že identita byla vzata a stažena, nebo vzata a dosud nezapsána, a nic nepozná jedno či druhé od čísla, které nikdo nikdy nepoužil — 0034 tohoto repozitáře není na žádné větvi, v žádném tagu ani v žádném pracovním stromu a ADR 0035 ji cituje. Mezery se proto uvádějí jako spotřebované, s tím, co je cituje, pojmenovaným jako důkaz pro čtenáře. Shellový nástroj nemá vlastní identitu peera, takže relace, která ohlásila alokaci a pak se ptá, je přeskočena přes svůj vlastní nárok; výstup říká, kdy horní hranice pochází z nároku na nástěnce. propose alokuje přes stejný průzkum.

  • propose zapisuje status: proposed a nic jiného. Neexistuje způsob, jak zapsat accepted: --status skončí s 15 a jmenuje důvod, protože nástroj, který umí zapsat accepted, mění vlastní odvození v pravdu repozitáře a pozdější čtenář nepozná, která rozhodnutí skutečně učinil člověk. Identita se přiděluje pod zámkem nad adresářem sekce, takže dva worktree navrhující ve stejný okamžik dostanou dvě identity. Reference --from je <type>:<value> s typem jedním z decision, session, commit, issue, file, test; cesta file: nebo test:, která neexistuje, skončí s 2. Odkazovaný důkaz činí záznam provenance.origin: extracted a extrahovaný záznam bez důkazu se odmítne jako pouhé tvrzení. --supersedes zapisuje obě poloviny vztahu, takže řetězec lze projít z obou konců.

  • related je druhá polovina odkazů záznamu a je autorská, nikoli zapisovaná propose: rule:<id>, claim:<id>, file:<path>, test:<path> — co rozhodnutí uvedlo v platnost, na rozdíl od provenance.derived_from, což je to, odkud pochází. Každý je validován tam, kde podle svého typu cíl žije, a extraktor z něj udělá hranu grafu (declares, supports, references, tested_by), takže opačný směr — které rozhodnutí uvedlo toto pravidlo v platnost — je knowledge edges, a ne druhý seznam, který by někdo udržoval v souladu.

  • check validuje každý záznam proti seznamu povolených klíčů generovanému ze schématu (žádný neznámý klíč), schema: adr/v1, uzavřené množině stavů, id, jehož číslo se rovná prefixu názvu souboru, a povinným sekcím těla; a napříč množinou: duplicitní identity, záznam superseded bez superseded_by, jednostranné nahrazení a odkaz, který se nikam nepřeloží. Při jakémkoli selhání skončí kódem 10.

  • check klade i otázku, na kterou žádný jednotlivý strom o sobě odpovědět nemůže: pro každou identitu, kterou tento strom oproti základu přidává (origin/master, origin/main, master, main, v tomto pořadí; MJ_ADR_BASE přepíše a je pak jediným kandidátem), nese jiný ref odlišný dokument na téže identitě? Odlišný název souboru je konkurenční nárok a odmítne se se jmenováním obou dokumentů, refů, které nesou ten druhý, a majordomus adr next; stejný název souboru na dvou refech je úprava a řeší se sloučením. Verdikt vždy uvádí svůj předmět — kolik identit bylo přidáno a proti čemu se měřily — a když se základ nevyřeší, říká, že ostatní refy nebyly prozkoumány, místo aby skončil čistě, aniž by cokoli řekl. doctor provádí totéž zkoumání a doctor je pre-commit hook, takže kolize se odmítne na větvi, a ne až při merge, kde by generate --strict vyloučil každého žadatele a jmenoval jen identitu. ADR 0053 je zdůvodnění.

  • affected čte sadu změn — ve výchozím stavu pracovní strom proti HEAD, --staged pro index, --base <ref> pro <ref>..HEAD plus pracovní strom — a jmenuje každé rozhodnutí, jehož vlastní soubor se změnil nebo jehož related jmenuje cestu, které se změna dotýká (odkaz na adresář pokrývá vše pod ním). Každá položka je poznámka k revizi WARN a exit kód zůstává 0: zda rozhodnutí stále platí poté, co se pohnul kód, který řídí, je úsudek, který nástroj činit nesmí. Je to obrácená otázka, na kterou odpovídá graf — co rozhodlo o tomto souboru? — položená ze sady změn, a ne ze záznamu.

doctor a watch spouštějí stejné zkoumání přes doktrínu majordomus.adr-integrity. Práh pro zaznamenání rozhodnutí vůbec je pravidlo majordomus.decision-threshold, o kterém rozhoduje revizor: nástroj validuje záznamy a hlásí, čeho se změna dotýká, a nikdy netvrdí, že ví, že diff ztělesňoval rozhodnutí.

$ majordomus adr propose "The registry is the one canonical declaration" --from file:docs/CAPABILITIES.md
proposed: adr-0011  The registry is the one canonical declaration
.ai/repo/adrs/0011-the-registry-is-the-one-canonical-declaration.md
status is "proposed"; accepting it is a person editing that field.

$ majordomus adr check
examined 11 decision(s) in .ai/repo/adrs/
every identity unique, every status known, every reference resolves

majordomus usecase

Spustitelné případy užití repozitáře: objekty Markdown pod sekcí use-cases manifestu, každý je úloha, kterou někdo provádí nástrojem, příkazy, pravidla, tvrzení, odpovědnosti a aplikace, které jmenuje, a scénář, který ji dokazuje proti bin/majordomus. docs/USE_CASES.md je smlouva; tato sekce je příkaz.

majordomus usecase list [--json]
majordomus usecase show <id>
majordomus usecase validate [--json]
majordomus usecase run [<id>...] [--json] [--out <dir>] [--keep]
majordomus usecase coverage [--json] [--check]
majordomus usecase impact [--base <ref>] [--json]
majordomus usecase scaffold [--missing] [--for command:<name>] [--dry-run]

list vypíše každý případ užití s jeho kategorií, stavem, tím, zda má scénář, a příkazy, které jmenuje; show vypíše jeden soubor a výsledek jeho posledního běhu. validate rozřeší každý odkaz (příkaz proti dispatch tabulce, doktrínu proti registru, tvrzení proti docs/CLAIMS.yaml, odpovědnost proti docs/RESPONSIBILITIES.yaml, aplikaci v obou směrech, kategorii proti taxonomy.yaml, setup skript a stdin tělo proti fixtures, nástroj MCP proti registru spustitelného souboru), odmítne klíč, který schéma nedeklaruje, id, které není názvem souboru, duplikát, tělo bez svých sekcí, krok scénáře spouštějící příkaz, který případ užití neuvádí, a aktivní případ užití mířící na záruku bez scénáře. doctor aplikuje stejnou validaci pod majordomus.catalogue-integrity.

run provede každý scénář v jednorázovém repozitáři připraveném jeho setup skriptem: každý krok je jedno skutečné vyvolání s argv ze souboru, jeho kód ukončení a výstup se ověřují a důkazy, normalizované (cesty, časová razítka, id, hashe, doby trvání), se zapíšou do .ai/local/evidence/use-cases/<id>.json; --out je zkopíruje jinam, --keep ponechá repozitář k prozkoumání a událost use_cases.ran se připojí do knihy. Případ užití bez scénáře se hlásí jako popsaný, ne spuštěný.

coverage sečte každý veřejný příkaz registru, každé zaručené tvrzení s odpovědností a každý nástroj MCP, který spustitelný soubor promítá, proti aktivním případům užití, které je pojmenovávají, spouštějí a mají úspěšné důkazy; use_cases.coverage politiky říká pro každou třídu, zda je mezera required, advisory nebo off; --check skončí s kódem 10 při požadované mezeře, stejně jako doctor, check a finish pod majordomus.use-case-coverage. impact mapuje soubory změněné od --base (ve výchozím stavu upstream) a v pracovním stromu na příkazy, pravidla, případy užití, scénáře a behaviorální případy, kterých se dotýkají, a pojmenuje další běh, který je třeba provést. scaffold zapíše koncept případu užití pro mezeru z toho, co už vědí registr, fixture příkazu a tvrzení; koncept projde validací a spustí se, ale nikdy se nezapočítává.

Živý krok se posuzuje podle celého svého vektoru argumentů, ne podle prvního slova. class příkazu v share/commands.yaml je to nejhorší, co udělá kterýkoli z jeho vektorů argumentů, takže knowledge je jako celek state-mutating, ačkoli knowledge edges, nodes, sources, candidates a check nic nezapisují, a check je read-only, ačkoli check --checkpoint zaznamenává checkpoint. Kde se podpříkazy nebo přepínače příkazu liší, registr nese seznam effects: položka subcommand nahradí třídu příkazu pro daný podpříkaz a položka flag ji zvýší pro každý vektor, v němž se přepínač objeví; přepínač třídu nikdy nesníží. usecase validate odmítne živý krok, jehož vektor není read-only, a jmenuje, co rozhodlo (by command, by subcommand 'derive' nebo by flag '--checkpoint'), a usecase run --live klade stejnou otázku znovu před spuštěním kroku, takže krok, který by zapisoval, se hlásí jako nesplněný a nespustí se. doctor sladí každý efekt s příkazem, na kterém je deklarován: podpříkaz musí být slovem ze syntaxe příkazu, přepínač v ní musí být zapsán a přepínač nesmí být deklarován jako read-only.

Exit kódy: 0; 2 při chybě použití; 10, když selže validace, scénář nebo povinná mezera v pokrytí; 12, když chybí sekce, případ užití nebo registr; 13, když nelze vytvořit repozitář scénáře.

majordomus bench

Změří každý veřejný příkaz registru za studena i za tepla a porovná se základní hodnotou.

Cíle jsou veřejné příkazy share/commands.yaml a nic jiného: příkaz přidaný do registru je cílem od té chvíle a harness nikdy neměří sám sebe. Každý cíl běží v jednorázovém repozitáři. Je-li přítomna vlastní sada nástroje, scénářem je první scénář fixture příkazu pod test/fixtures/commands/, takže měří se to, co web předvádí a sada provádí; jinak je to holý příkaz v nainstalovaném repozitáři.

Příkaz jen pro čtení se spustí jednou za studena, poté benchmark.warmupkrát bez vzorkování a poté benchmark.sampleskrát za tepla, vše v jednom repozitáři. Příkaz, který mění stav, dostane pro každý vzorek čerstvý repozitář, takže každý jeho vzorek je za studena a nemá rozdělení za tepla. Vzorek je pouze doba běhu podřízeného procesu podle nástěnných hodin; příprava se do měření nikdy nezapočítává. Každé rozdělení se zaznamenává s počtem, minimem, p50, p90, p95, p99, maximem, průměrem a směrodatnou odchylkou; nic se nezprůměruje do ztracena.

  • bench změří každý cíl; bench <command>... jen ty.
  • --list vypíše cíle s jejich třídou a scénářem, --format json jako data.
  • --samples <n>, --warmup <n> přepíšou politiku jen pro tento běh; --mode cold|warm|both vybírá zaznamenané distribuce.
  • --format json vypíše běh jako jeden dokument se schématem majordomus/benchmark-result/v1: id běhu, commit a zda byl strom špinavý, platformu, profil a jeden výsledek na cíl a režim.
  • Každý běh se zapíše pod .ai/local/benchmarks/ (runs/<run-id>.json, latest.json, jeden řádek na běh v history.jsonl), pokud --no-save. To je lokální důkaz: ignorovaný gitem, nikdy základní hodnota.
  • --write-baseline zapíše běh pod .ai/repo/benchmarks/rust/ jako baseline.<platform>.json — jedna základní hodnota na platformu, protože doba porovnávaná mezi stroji porovnává stroje — se schématem majordomus/benchmark-baseline/v1 a vypíše stará a nová p50/p95/p99 pro každý cíl. Odmítne špinavý strom bez --force, protože základní hodnota zaznamenává commit.
  • --check porovná běh s baseline pod benchmark.regression: pro každý cíl a režim je p50, p95 nebo p99 o více než prahový zlomek nad baseline FAIL jmenující metriku, obě hodnoty a práh a příkaz skončí s 10. Bez baseline skončí s 12; baseline s jiným schématem není srovnatelná a skončí s 15.

MJ_TIMING=1 u jakéhokoli příkazu vypíše na stderr fáze a čítače práce daného běhu, čímž se pomalý cíl rozebere na části.

$ majordomus bench --list
command      class                      scenario
init         generated-output-mutating  fixture fresh
doctor       read-only                  fixture not-wired
...

$ majordomus bench doctor version
command      mode  status       n     p50     p95     p99     max  scenario
doctor       cold  ok           1    2711    2711    2711    2711  fixture not-wired
doctor       warm  ok          10    2640    2790    2790    2790  fixture not-wired
version      cold  ok           1      41      41      41      41  fixture prints
version      warm  ok          10      38      45      45      45  fixture prints

slowest by warm p95 (cold where warm does not apply):
  doctor       p95 2790 ms
  version      p95 45 ms

run b-20260905T031200Z-9f1c saved as .ai/local/benchmarks/runs/b-20260905T031200Z-9f1c.json

Kód 2 při chybě použití, 12 když pojmenovaný cíl není veřejný příkaz registru, 13 když cíl neproběhl čistě (jeho řádek říká setup-failed nebo návratový kód, který vytvořil), 10, 12 a 15 z --check jako výše.

majordomus archive

Pořiďte snímek sledovaného stromu, který lze číst nebo ověřit jinde.

Repozitář opouští stroj častěji, než se zdá: k jazykovému modelu, který si ho přečte celý najednou, k recenzentovi, který nemůže klonovat, k auditorovi, který musí spustit kontroly na kopii. Dělalo se to ručně, pokaždé jinak, a ruční verze chybovala ve dvou věcech, které vyplavou až na druhém konci.

Putuje git index a jen git index. Nic nesledovaného se nikdy nearchivuje — ani výstup sestavení, ani cache, ani .ai/local/, což je vlastní stav tohoto checkoutu a nikdy se nesdílí. To je jedno pravidlo místo seznamu výjimek a díky tomu je bezpečné příkaz namířit na repozitář, který nikdo nečetl.

Co se vynechává nad to, je deklarováno, ne kódováno. share/archive.yaml drží profily; repozitář může přidat nebo nahradit jeden ve vlastním archive.yaml pod .ai/repo/. Profil, který odhazuje odvozené cesty, čte .gitattributes — každá projekce v tomto repozitáři je tam už označena merge=derived, z jiného důvodu — místo aby držel druhý seznam generovaných cest, což je duplikace, kterou .ai/repo/rules/project/commands-are-projections.v1.md odmítá.

Režimy se nesou výslovně. zip -X odstraní extra pole, která drží unixový režim, a rozbalený strom pak neobsahuje žádný spustitelný soubor: každý skript selže při spuštění, a na druhém konci to nelze odlišit od toho, že je repozitář rozbitý. Proto se režim každé položky čte z indexu — autority, ne z pracovního stromu —, zapisuje do _ARCHIVE/MANIFEST.txt a _ARCHIVE/restore.sh uvnitř archivu režimy vrací a vytváří místní git index, protože zdejší kontroly vyjmenovávají repozitář pomocí git ls-files a bez indexu nezkoumají nic a hlásí, že nic není špatně.

Každý archiv se čte zpět dřív, než se příkaz vrátí: počet položek musí odpovídat tomu, co bylo připraveno, a pokud spustitelné soubory dovnitř vstoupily a ven nevyšly, je to selhání, ne překvapení pro příjemce.

Čte: share/archive.yaml, archive.yaml pod .ai/repo/, když ho repozitář má, .gitattributes a index gitu. Zapisuje: tmp/archives/ — nebo kam řekne --out.

Dodávané profily:

ProfilPro
contextModel, který bude číst repozitář. Každý sledovaný zdroj, bez projekcí z něj generovaných.
auditSpuštění vlastních kontrol repozitáře na kopii. Každý sledovaný soubor, nic vynecháno, režimy a git index obnoveny.
governancePouze provozní kontrakt: .ai/, docs/, AGENTS.md, CLAUDE.md a žádný kód.
$ majordomus archive --dry-run
archive: profile context — 2299 of 2757 tracked file(s), 12919 KB of content
         left out: 10 (binary)
         left out: 443 (derived)
         left out: 5 (excluded)
         nothing written (--dry-run)

$ majordomus archive
OK   archive     prismatic-majordomus-context-20260911.zip — 2303 entr(ies) read back, 76 of them executable
archive: profile context — 2299 of 2757 tracked file(s), 12919 KB of content
         wrote tmp/archives/prismatic-majordomus-context-20260911.zip (5044 KB)

Behaviour:

  • --list vypíše profily s tím, k čemu každý slouží, a označí výchozí.
  • --dry-run hlásí výběr a důvod, proč byl každý soubor vynechán, a nezapisuje nic.
  • --out přepíše cíl; --format zip|tar.gz přepíše kontejner.
  • Existující výstupní soubor se odmítne s 15, pokud není zadáno --force, aby nešel pod rukama nahradit archiv, který někdo právě nahrává.
  • Cesta, kterou index jmenuje a pracovní strom už nemá, se počítá a hlásí, nechybí tiše.
  • --json vypíše jeden objekt s počty, důvody a cestou.

Skončí s 2 při chybě použití, s 12, když takový profil neexistuje nebo zip není na PATH, s 10, když profil nevybere žádný ze sledovaných souborů, s 15, když výstup existuje, a s 13, když se kontejner nepodařilo zapsat nebo se nenačte zpět jako to, co do něj vstoupilo.

majordomus version

Vypsat verzi a skončit. --version se přijímá jako synonymum a version funguje bez instalace: nikdy nečte .ai/.

Čte: share/version.txt vedle nástroje — nikdy ten, který pojmenovává MAJORDOMUS_SHARE. Zapisuje: nic.

Behaviour:

  • Vypíše majordomus <version> na standardní výstup a skončí kódem 0.
  • Verze se píše v apps/majordomus-cli/Cargo.toml; share/version.txt je její generovaná projekce, dodávaná v každém archivu, a tento příkaz ji vypíše. Patička veřejného webu čte stejnou odpověď.
  • Skončí s kódem 12 a jmenuje soubor, když distribuce nenese share/version.txt.

Integrace hooků (cíl)

init je vypíše; doctor ověří, že jsou přítomny a spustitelné:

# .git/hooks/pre-commit  (or the file named by core.hooksPath)
majordomus doctor || exit $?

# .git/hooks/pre-push
majordomus finish --check || exit $?

Kterýkoli řádek může místo toho žít v podhooku dispečerského hooku — doctor se dívá i do <hook>.d/ a pojmenuje soubor, který našel. Repozitář předcházející Majordomus nepotřebuje žádný bootstrapový příznak: finish --check projde, dokud není aktivní žádná úloha, takže hooky jsou netečné až do prvního start.

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.