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.

Kontinuita

jak práce přežije relaci, která ji vykonává: trvalé záznamy, proč přepisy nejsou stav, jak se vybírá kontext a rozvazují záznamy

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

Konverzace s workerem AI není místo, kam cokoli ukládat. Skončí, zkompaktuje se, nahradí ji jiný worker s jiným kontextovým oknem a nic z toho nemáte pod kontrolou. Práce mezitím musí pokračovat.

Odpověď Majordomus zní, že provozní stav žije v repozitáři, v souborech, které přežijí každou konverzaci, a že další pracovník dostane brífink sestavený z těch souborů, a ne přepis k opětovnému čtení.

Conversation is ephemeral.
Operational state is durable.
Context is selected.
History is reconstructable.
Handovers are explicit.
Completion is independently verified.

Problém, přesně vyjádřený

Worker se zastaví. Něco musí přežít, jinak další worker začíná rekonstrukcí toho, co věděl předchozí — z commit zpráv, z diffu, z lidské paměti na rozhovor, kterého se neúčastnil. Ta rekonstrukce je pomalá a horší: je sebejistě špatně dost často na to, aby to záleželo.

Zřejmá oprava je uložit konverzaci. Ta oprava je špatná ze tří důvodů.

Přepis není stav. Je to záznam o tom, jak někdo dospěl k závěru, včetně každé chybné odbočky, v pořadí optimalizovaném pro nic. Další worker potřebuje závěr, aktuální fakta a další krok.

Přepis je neomezený. Roste s délkou relace, ne s velikostí práce, a kontext desáté relace je plný chybných začátků té první.

Přepis tiše zastarává. Zaznamenává, co platilo v době jeho napsání, a neřekne nic, když repozitář postoupí dál. Worker, který věří zastaralému vyprávění, se rozhoduje proti repozitáři, který už neexistuje.

Majordomus tedy nic z toho neukládá. To, co ukládá, je malé, typované a každá část má jeden domov.

Trvalé záznamy a ty, které nemají tvar úlohy

ZáznamOdpovědiMěnitelnostKde
úlohana čem se pracuje, za jakých omezení a v jakých cestáchměnitelné; jedno aktivní na checkoutstate/current.yaml
kontrolní bodco platilo před několika minutami a co přijde dálappend-onlystate/checkpoints/
handoverzáměrný balíček pro toho, kdo pokračujeappend-onlystate/handovers/
rozhodnutíco se rozhodlo, proč a co se zamítloappend-onlystate/decisions.md
otevřená otázkaco je nevyřešeno a co to blokujeměnitelný index; kniha drží historiistate/open-questions.md
událost historieco se stalo, kdy, pro kterou úlohu, v kterém commituappend-onlystate/ledger.jsonl

Prompt assety v .ai/repo/prompts/ jsou sedmá věc, ale nejsou záznamy o práci — jsou to znovupoužitelná zarámování, verzovaná s repozitářem.

Druhy, které nemají tvar úlohy: relace a to, co se naučila

Výše uvedené záznamy mají všechny tvar úlohy. Každý visí na práci, což je správně, a zbývá jedna otázka, která nemá kde žít: co udělal jeden worker mezi usednutím a zastavením?

Ta otázka není totéž co kterákoli z těch. Úloha může přežít workera; worker se může za odpoledne dotknout tří úloh. Nic v těch záznamech nezachytí, že dvě rozhodnutí s hodinovým odstupem, založená pod různými úlohami, učinil tentýž člověk v jednom sezení — a to je přesně ta příčinná nit, kterou se pozdější čtenář snaží zachytit.

ZáznamOdpovědiMěnitelnostKde
sessionco udělala jedna epizoda provádění, mezi kterými commity, s vytvořením kterých záznamůjedna otevřená na relaci poskytovatele, poté neměnnálocal/state/sessions-open/<provider session>.yaml, na které ukazuje local/state/session-current.yaml, poté repo/sessions/ vrstvy
pracovní kontextco bylo pracovníkovi řečeno při otevření epizody a co si poznamenal při prácijen se připisuje, nikdy se nepřepisujelocal/session-contexts/<stamp>--<session-id>.md
záznam znalostíco repozitář díky epizodě nyní ví: každé zaznamenané rozhodnutí, vyřešená otázka a dokončená úloha, jako jedno tvrzení s původemkandidát je přepisován deriverem, dokud ho člověk nepovýší nebo neodmítne; ověřený je pak neměnný, leda nahrazenímrepo/knowledge/candidates/<id>.md, potom repo/knowledge/curated/<id>.md

Nejde o týž záznam a odpovídají na opačné otázky. Uzavřená relace říká, co epizoda vyprodukovala, odvozeno z gitu a účetní knihy, a je sdílená. Pracovní kontext říká, co epizoda dostala, zmrazeno z builderu při otevření, a je lokální: jmenuje tento stroj a jeho pozdější přerozřešení by vyrobilo jiný dokument, takže ho žádný povrch nemůže reprodukovat a žádný nepublikuje (ADR 0015).

Relace se otevře, může přejít přes několik úloh a zavře se. task != session v obou směrech: úloha zasahující dvě relace je jmenována oběma a relace zasahující dvě úlohy jmenuje obě.

Relace je obálka, ne vyprávění

Uzavřený záznam je identita, časová hranice a reference. Jmenuje úlohy, úkoly, milníky, checkpointy, předávky, rozhodnutí, otázky a důkazy své epizody a žádné z nich netěluje. Autorský souhrn je dovolen a není autoritou; autoritou jsou záznamy, na které ukazuje.

Pokušením je epizodu místo toho sepsat — jeden dokument s citovanými rozhodnutími, vloženými těly checkpointů, vloženým git logem a vyprávěným uvažováním. Takový dokument je přepis s lepším formátováním a selhává týmiž třemi způsoby jako každý přepis: je záznamem toho, jak někdo někam dospěl, a ne toho, kde je, roste s délkou relace, a ne s velikostí práce, a tiše zastarává, zatímco repozitář jde dál.

Soubor relace tedy drží ukazatele a ukazatele se kontrolují. Odkaz na checkpoint, který neexistuje, je hlášená vada, ne rozbitý odkaz, kterého si nikdo nevšimne.

Obálka se odvozuje, nenahromaďuje

Nic nezapisuje do otevřené relace. checkpoint, decision, question a plan jsou nezměněny a o relacích nevědí nic. Při uzavření se seznamy odkazů počítají z ledgeru.

Kniha je už jen k připisování, už ji zapisuje jen Majordomus, už je řazena pořadím, v jakém příkazy běžely, a už je validována. Zná každou skutečnost, kterou obálka potřebuje. Nechat každý příkaz navíc připisovat do souboru relace by dalo zápis na horkou cestu příkazů, které dnes připisují jeden řádek, a vytvořilo by druhý měnitelný účet událostí, které kniha už drží — což je druhý zdroj pravdy, který celý tento návrh odmítá. Cenou je, že kniha je nyní nosná pro dva účely, a to je řečeno, ne skryto.

Co vybírá a proč to není časový rozsah, stojí za vyslovení, protože zřejmá implementace je špatná. Každý řádek ledgeru nyní nese relaci, která jej zapsala, vedle commitu a větve, které už nesl. První verze místo toho vybírala každý řádek mezi dvěma časovými razítky relace a když poprvé běžela doopravdy, prohlásila úlohy, checkpointy a předávky jiného workera: ledger je jeden soubor na repozitář, dva workery do něj zapisovali a žádné časové razítko je nerozliší. Která epizoda událost zapsala, ví stroj v okamžiku zápisu, takže se zaznamená tehdy.

Řádek bez relace nepatří do žádné epizody. Relace jsou volitelné a práce provedená mimo ně se připisuje nikomu, ne tomu, kdo měl poblíž otevřenou relaci.

Relace se čtou podle stejných pravidel jako všechno ostatní

Rozlišení je dvoustupňové pravidlo: stejný repozitář, stejný worktree, stejná větev; pak stejný repozitář, stejná větev; pak nic. Relace z jiné větve se nikdy nenabídne.

Divergence používá stejné čtyři popisky — exact, advanced, diverged, different_context — vypočtené při čtení z commitu, na němž se relace uzavřela. Relace, jejíž větev byla pod ní přepsána, je označena diverged všude, kde se vypisuje, a nikdy se tiše nepředkládá jako aktuální znalost.

Pořadí vychází ze zaznamenaného časového razítka, přičemž pořadí řádků ledgeru rozhoduje remízy uvnitř jedné sekundy. Nic nečte čas změny souboru v souborovém systému: nepřežije klon a není to čas, který záznam tvrdí.

Čím relace není

Není to rozsah. Úloha si nárokuje cesty; epizoda ne, a kdyby se jí cesty daly, vznikl by druhý, slabší nárok, s nímž by se kontrola koordinace rozcházela.

Není to jednotka akceptace. finish vyhodnocuje úlohu proti její smlouvě. Uzavření relace tvrdí jen to, že epizoda skončila.

Není povinné. Worker, který nikdy neotevře relaci, ztratí hranici epizody a nic jiného; každý jiný záznam se zapíše přesně jako dříve.

Checkpoint a předávka jsou různé objekty

Snadno se pletou a pletení je drahé.

Checkpoint je poznámka o postupu epizody. Píše se často, podle politiky je krátký (checkpoint.max_body_lines) a jeho úkolem je být celý citovatelný v dalším briefingu. „Reprodukována chyba s fixture; příčina je v normalizaci, ne v porovnání; dále napsat regresní test.“

Předávka je záměrný balíček pokračování, který se zapisuje, když pracovník končí. Má povinné sekce, odmítne se, pokud je kterákoli prázdná, a je to to, z čeho jiný pracovník pokračuje. Zapisuje se zřídka.

Ani jeden nepatří úloze. Oba pojmenovávají úlohu, pod kterou byly zapsány, když nějaká je, a zapisují se i tehdy, když není. Úloha je jednotka zamýšlené práce, kterou někdo otevírá a uzavírá; epizoda je hranice konverzace poskytovatele, která začíná, když se klient připojí, a končí, když se odpojí. Nechat záznamy záviset na první znamená, že běžný akt dokončení úlohy vypne kontinuitu pro každou epizodu po ní — což není hypotetické. Mezi 2026-09-05 a 2026-09-11 tento repozitář nezapsal ani jeden z nich, protože úloha byla označena handed_over a tři samostatné ochrany se každá, správně podle svého kontraktu, ptaly, zda je úloha active. ADR 0052 odstranilo předpoklad, který sdílely.

Checkpoint, který vyroste v report, se odmítne místo zkrácení, s návrhem napsat místo toho předávku. Strop je mechanismus, který oba objekty drží odlišné.

Záznam znalostí není ani jedno. Checkpoint říká, jak práce vypadala, a předávka, co dělat dál; záznam znalostí říká, co je nyní pravda o repozitáři kvůli epizodě — rozhodnutí, které zaznamenala, otázka, kterou vyřešila, důvod, proč byla úloha blokována — jako jedno tvrzení s třídou, stavem a provenienci, která se vyřeší. Odvozuje se ve stejných okamžicích jako checkpoint, z knihy a gitu a nikdy z konverzace ani z žádného z obou dalších záznamů, a přistává ve sledovaném stromu jako kandidát, kterého osoba povýší nebo zamítne. Je to jediný záznam epizody, který přežije klon dřív, než kdokoli napsal ručně slovo, a jediný, který deriver nikdy nepíše sám: verified je akt osoby. KNOWLEDGE.md má odvození a příkazy.

Identita pochází z reality

repository_id, worktree, branch, head, working_tree a changed_files na každém záznamu se počítají z gitu při zápisu. Tělo, které obsahuje kterékoli z nich, se odmítne.

Není to pedantství. Worker, který má shrnout vlastní stav, vytvoří věrohodné shrnutí, a věrohodné shrnutí obsahující hash commitu, který worker neověřil, je horší než žádné shrnutí, protože vypadá ověřitelně. Pole, která stroj dokáže určit, určí stroj; pole, která dodá jen člověk nebo worker, dodají oni a jsou tak označena.

Záznamy se čtou se štítkem divergence

Nic se nedůvěřuje jen proto, že existuje. Při čtení se zaznamenaný head porovná se současným HEAD přes git merge-base a záznam se označí:

PopisekVýznamCo dělat
exactzapsáno při tomto commitudůvěřovat
advancedgit se od té doby posunul vpředvěřte tomu, očekávejte, že něco z toho bude hotové
divergedzaznamenaný commit není předekhistorie byla přepsána; důvěřujte gitu, ne záznamu
different_contextjiná větevtento záznam není o vaší práci

context vypíše štítek. check a watch ho hlásí jako nález. Záznam je důkaz, nikdy autorita.

Rozlišení je deterministické a nepřítomnost je platná odpověď

handover --resolve, checkpoint --show a context všechny používají stejné pravidlo:

  1. Stejný repozitář, stejné worktree, stejná větev.
  2. Stejný repozitář, stejná větev, když větev není odpojená.
  3. Nothing.

Třetí úroveň neexistuje. Záznam z nesouvisejícího worktree nebo větve se nikdy nenabízí, protože globálně novější záznam, který se tiše stane vaším kontextem, je horší než žádný kontext — nepoznáte, že je špatný, dokud podle něj nezačnete jednat.

Záznamy zapsané ve stejné sekundě by se jinak vyřešily v pořadí určeném náhodnou příponou názvu souboru, a tak shodu rozhoduje registr, který je pouze přidávací a zapisuje se v pořadí příkazů.

Když nic nesedí, odpověď je „žádná relevantní předávka“, ne nejbližší dostupná věc. Absence je lepší než nesprávná paměť.

Kontext se vybírá, nehromadí

majordomus context sestaví briefing v pořadí autority — git, pak úloha a její profil, pak blokátory, pak autorské záznamy, pak historie událostí — a vytiskne jej. Nic se neukládá; počítá se znovu pokaždé ze záznamů a gitu.

Co se zahrne, rozhoduje blok context profilu. Úloha routine nepotřebuje historii rozhodnutí; úloha deep-work chce rozhodnutí repozitáře, a ne jen vlastní. Tyto přepínače byly konfigurací, kterou nic nečetlo, dokud nevznikl builder; teď jsou tím, co rozhoduje.

Když složený text překročí context.builder_budget_lines, sekce se zahazují v pevném pořadí: nejdřív historie, pak dotčené soubory, pak rozhodnutí, pak těla checkpointu a předávky, která degradují na ukazatel, místo aby zmizela. Git, úloha, profil a otevřené blokátory se nezahazují nikdy.

Každé vyloučení je jmenováno, s důvodem, pod EXCLUDED. Nedostatečně naplněný kontext se ladí čtením toho seznamu, ne hádáním, co nástroj rozhodl vynechat. Hlášený počet řádků je počet skutečně vytištěného dokumentu, včetně jeho vlastní hlavičky a patičky.

Historie slouží k rekonstrukci, ne ke zpětnému čtení konverzace

state/ledger.jsonl je jen k připisování a zapisuje do něj pouze Majordomus. Jeden řádek na událost: zahájeno, kontrolní bod, zaznamenáno rozhodnutí, otevřena otázka, vyřešena otázka, předáno, dokončeno, projekce aktualizovány, kniha rotována. Každý nese časové razítko, úlohu a commit, u kterého se to stalo.

majordomus history to čte zpět s filtry. Odpovídá, co se stalo, kdy, pro kterou úlohu, v jakém stavu gitu, jaké ověřování běželo a jaký výsledek byl přijat. Neodpovídá, co kdo řekl, protože to se nezaznamenává.

Retence je záměrná: ledger má strop řádků a history --rotate přesouvá nejstarší řádky do datovaného archivního souboru. Nikdy nemaže a odmítne přepsat existující archiv.

Překážky jsou stav, ne próza

Nevyřešená otázka v odstavci předávky je poznámka. Položka v state/open-questions.md zamítá finish --outcome completed — jakákoli položka, nejen ta, kterou otevřela aktivní úloha. Otázka je člověk, který dluží odpověď, a práce, kterou blokuje, přežije úlohu, která se ptala; kontrola, která by na hranici úlohy zapomněla, by byla kontrolou, kterou předávka obejde. Úložiště je lokální pro checkout, v ignorovaném .ai/local/state/, takže otázky, které kontrola vidí, jsou ty položené v této pracovní kopii; otázka se do jiného checkoutu dostane předávkou, která ji jmenuje, nikdy gitem.

Odmítnuto je jen completed. blocked, partial, no_match a failed jsou poctivá prohlášení, že práce nebyla dokončena, a jejich odmítnutí by koupilo zelenou kontrolu vynucením špatně označeného výsledku.

Ta kontrola je důvodem, proč má soubor strojově psaný formát řádku a proč check, doctor a watch selžou na položce, která se neparsuje: kontrola, která nepřečte položku, je kontrola, kterou lze obejít překlepem.

Nic zde nevolá model

Majordomus ukládá, validuje, řeší, promítá a ověřuje. Neshrnuje, nerozhoduje, co má checkpoint říkat, a nevolá žádnou síť. Chcete-li, aby checkpoint napsal model, nechte ho napsat workera a pošlete ho do roury. Nástroj je deterministická infrastruktura a tou zůstane.

Kdo provozuje životní cyklus

Startovní událost také konverguje ke sdílenému serveru. S session.ensure_server_on_start (ve výchozím stavu zapnuto) spustí majordomus serve ensure: server se spustí jako samostatný proces, když na tento checkout nikdo neodpovídá, nikdy se nesestavuje a briefing nese jeden řádek, který říká, jak to s ním je — Shared server: ready http://127.0.0.1:8741 pid 123, nebo not ensured: the executable is not built (run \just build`). Discovery, loading and now the server are three things a worker no longer has to remember; docs/MCP.md` popisuje životní cyklus.

Nic výše nevyžaduje, aby si worker cokoli z toho pamatoval.

Tam, kde poskytovatel ohlašuje hranice sezení, životní cyklus řídí jeho vlastní háky. Událost startu otevře epizodu, zmrazí kontext, který builder v tu chvíli vyřešil, a zapíše briefing na standardní výstup — který poskytovatel přidá do kontextu, který se chystá sestavit. To je jediný okamžik, kdy záznam o pokračování dorazí k pracovníkovi, aniž by o něj požádal, a je to rozdíl mezi záznamem, který se zapisuje automaticky, a záznamem, který se také čte.

flowchart TD
  ss["provider fires SessionStart"]
  opens["session opens (or the open one is kept)"]
  frozen["the working context is frozen"]
  brief["the briefing goes to stdout: the episode, the resolved<br>handover with its divergence label AND its freshness,<br>the blockers, and — only when the record is still<br>current — the one section to act on"]
  work["work happens"]
  pc["provider fires PreCompact"]
  cp1["a derived checkpoint, because the conversation<br>is about to stop holding it"]
  se["provider fires SessionEnd"]
  cp2["a derived checkpoint, for the same reason: the close<br>is the other moment at which what the episode knows<br>stops being reachable"]
  kn1["derived knowledge candidates, from the ledger and git:<br>what the episode decided, resolved and finished"]
  kn2["derived knowledge candidates, on the close path,<br>before the envelope is sealed"]
  ho["a derived handover"]
  env["the episode closes into its envelope of references"]

  ss --> opens
  ss --> frozen
  ss --> brief
  ss --> work
  work --> pc
  pc --> cp1
  pc --> kn1
  pc --> se
  se --> cp2
  se --> kn2
  se --> ho
  se --> env

Každá z těch šipek se spustí bez ohledu na to, zda je úloha otevřená, a bez ohledu na výsledek poslední úlohy, a každá událost zapíše řádek provider.event.received do knihy dříve, než se o kterékoli z nich rozhodne. Hook musí svému poskytovateli vrátit úspěch — blokovat něčí práci je horší selhání — ale vrátit úspěch neznamená tvrdit, že se práce stala, a bez stvrzenky jsou potom událost, která se nikdy nespustila, a událost, která se spustila a nic neudělala, tím samým pozorováním.

Divergence není aktuálnost

Každý záznam nese dva nezávislé úsudky a odpovídají na různé otázky.

Divergence — exact, advanced, diverged, different_context — říká, kde commit záznamu leží vůči HEAD tohoto checkoutu. Freshness — fresh, aging, stale, unknown, invalid — říká, jak je starý, vůči session.freshness v politice, což je jediné místo, kde je kterýkoli práh zapsán.

Záznam je advanced v den, kdy je zapsán, a o měsíc později stále advanced: advanced znamená, že jeho commit je předkem HEAD, což zní jako shoda a o čase neříká vůbec nic. Záznam, který je zároveň advanced a stale, je přesný tvar výpadku z 2026-09-05 a je důvodem, proč se obojí hlásí vedle sebe.

Zastaralý záznam je historie. Briefing ho stále ukazuje — vědět, co dělal poslední pracovník, má cenu — ale je označen jako historický, uvede se jeho stáří a důvod a jeho Next Action se záměrně necituje jako to, co dělat nyní. Instrukce, která byla správná před šesti dny, předaná bez výhrad, je horší než žádná: pracovník nepozná, že je špatná, dokud podle ní nejedná.

Epizoda patří relaci poskytovatele, která ji otevřela, a ne checkoutu. Dvě okna téhož poskytovatele otevřená na jednom worktree jsou dva workeři: každá startovní událost otevře nebo ponechá svou vlastní epizodu, každá koncová událost zavře svou a žádnou jinou a řádky účetní knihy každého workera nesou jeho vlastní id epizody. Otevřená epizoda je local/state/sessions-open/<provider session>.yaml; local/state/session-current.yaml ukazuje na epizodu tohoto checkoutu a worker, který umí jmenovat svou relaci poskytovatele, rozřeší svou vlastní, místo aby následoval ukazatel. Dříve --if-open keep vracelo už otevřenou epizodu, ať událost říkala cokoli: sedm souběžných relací tu 2026-09-09 vyrobilo mezi sebou jediný záznam, označený tím id, které bylo náhodou první, a zavřený tím oknem, které se zavřelo první.

Briefing je ohraničen session.briefing_budget_lines a nese odkazy, štítky, jednu sekci a id kandidátů znalostí čekajících na posouzení na větvi — nikdy konverzaci. Každé ze tří chování je přepínač v politice a session.briefing_on_start: false obnoví dřívější mlčení.

Toto je jediná cesta, kterou se cokoli pod local/ dostane do kontextu modelu, aniž o to bylo požádáno. Je záměrně úzká a druhá polovina tohoto pravidla je bezpodmínečná: nic pod local/ se nikdy nepublikuje generátorem ani neposkytuje na veřejné ploše. .ai/repo/adrs/0017-an-episode-that-opens-is-handed-what-the-last-one-left.md zaznamenává proč.

Worker bez takového poskytovatele neztratí nic z modelu a všechnu automatizaci: každý příkaz níže je stejný a jejich spouštění je opět věcí pamatování.

Čtení zpět odjinud než z terminálu

continuity.state ve spustitelném souboru v Rustu čte tuto stejnou lokální polovinu a hlásí ji přes MCP (majordomus_continuity), přes HTTP (GET /api/v1/continuity) a na stránce Continuity v Cockpitu: otevřenou epizodu, aktivní úlohu, předávku a checkpoint, které se zde vyřeší, s jejich štítky divergence, a blokátory. Používá stejné dvě úrovně a stejné čtyři štítky a nikdy nezapisuje — životní cyklus má jednoho zapisovatele a druhý výklad událostí, které ledger již drží, je to, co tento návrh odmítá.

Obsluhuje se a nikdy nepublikuje. Tyto záznamy jmenují tento stroj, takže žádný generovaný dokument ani stránka webu žádný nenese, a test to prokazuje.

Jedna epizoda a pak všechny ostatní

continuity.state odpovídá o jedné epizodě: té, na kterou se vyřeší state/session-current.yaml. To je správná odpověď pro briefing — worker chce svou epizodu, ne sčítání — a špatná odpověď pro každého, kdo se ptá, zda subsystém funguje, protože ukazatel je symlink, který přesměruje nejnovější startovní událost. Dne 2026-09-11 tento repozitář držel pět otevřených epizod v jednom checkoutu a žádný povrch, který nástroj má, nedokázal jmenovat víc než jednu z nich. Epizoda, kterou nikdo nevidí, je epizoda, která se nikdy neuzavře.

Takže existuje druhé čtení, lifecycle.*, které čte úložiště, a ne ukazatel, a odpovídá na otázky operátora místo pracovníka:

SchopnostOdpovědiOdkud odpověď pochází
lifecycle.episodeskaždá otevřená epizoda, její relace poskytovatele, kde stojí (current, open, foreign, stranded), úlohy, kterých se dotkla, a co kniha viděla, že naposledy udělalastate/sessions-open/*.yaml, state/session-current.yaml, state/ledger.jsonl
lifecycle.recoveryepizody, které se už nedokážou samy uzavřít, dočasné soubory, které zabitý close zanechal ve sledované sekci, zda byl ukazatel migrován a zda je vyúčtována každá epizoda, jejíž start registr viděltentýž store plus sekce relací, kterou jmenuje manifest
lifecycle.runtimecommit, o kterém tento proces odpovídá, proti commitu, na kterém repozitář právě jestav gitu servírovaného indexu a git přečtené při volání
lifecycle.providerskteré události lifecycle adaptér každého poskytovatele deklaruje, zda umí archivovat prompty a které vynucovací záznamy tohoto repozitáře jsou zapojeny do jeho hookushare/providers.yaml a enforcement politiky
lifecycle.closedsledované záznamy, které klon obdrží: kolik, kolik na této branch a nejnovějších dvacetindex objektů, druh session

Každý je vystaven přes MCP a přes GET /api/v1/lifecycle/<name> a stránka Continuity v Cockpitu je jejich projekcí. Žádný nezapisuje: životní cyklus má jednoho zapisovatele a druhý výklad událostí, které kniha už drží, je to, co tento návrh odmítá.

Dvě věci mezi nimi záměrně nejsou.

Žádné hodiny a žádné prahy. Nic v lifecycle.* nerozhoduje, že je záznam starý. Stáří je session.freshness v politice a posuzuje je continuity.state; druhý stroj na to by tu byl druhým zdrojem pravdy, který způsobí, že se čísla rozcházejí. (session.freshness a popisky fresh | aging | stale | unknown | invalid patří k ADR 0052 a dorazí s ním; do té doby záznam nese svůj popisek divergence a zaznamenaný časový údaj a aritmetiku si udělá čtenář. Celý smysl ADR 0052 je v tom, že by to čtenář dělat neměl.)

Žádný odhad o připojení. Zda je poskytovatel, který otevřel epizodu, stále připojen ke své konverzaci, není fakt tohoto repozitáře. Soubor epizody nezaznamenává žádný proces, nástěnka peerů nezaznamenává žádnou epizodu a klient, který skončí bez spuštění koncové události, zanechá soubor shodný s tím, který používá živý worker. lifecycle.episodes hlásí to, co lze pozorovat — kde soubor je, čí worktree jmenuje, zda ho ukazatel sleduje a kdy ho účetní kniha naposledy viděla zapisovat — a stranded je vyhrazeno pro jediný případ, který repozitář skutečně může zjistit: worktree, v němž se epizoda otevřela, z disku zmizel, takže nic nemůže složit její záznam.

Celá cesta, jednou

Všechno výše je jedna cesta a stojí za to vidět ji celou. Každý box je věc, která existuje; popisky šipek jsou to, co musí platit, aby se dosáhlo dalšího boxu.

flowchart TD
  cmd["a person runs a command<br>or opens an editor"]
  conv["a provider starts a conversation<br>(Claude Code: SessionStart)"]
  entry["ENTRY<br>bin/majordomus, .envrc, or the hook shim"]
  runtime["RUNTIME ENSURED<br>serve ensure: one shared server per checkout,<br>started if none answers, never built"]
  client["CLIENT ATTACHED<br>MCP over stdio or HTTP; the peer board is what<br>the other workers can see"]
  episode["EPISODE OPENED<br>keyed by the provider session, not by the checkout:<br>state/sessions-open/&lt;provider session&gt;.yaml and the pointer<br>aimed at it. --if-open keep returns this provider<br>session's episode and no other."]
  fresh["FRESHNESS VALIDATED<br>the resolved handover and checkpoint carry two independent<br>labels: divergence (where their commit sits) and freshness<br>(how old they are). A stale record is shown as history;<br>its Next Action is not quoted as the thing to do now.<br>[ADR 0052]"]
  context["CONTEXT PROJECTED<br>the briefing, within session.briefing_budget_lines, frozen<br>into local/session-contexts/ as the working context —<br>what this episode was told, never rewritten"]
  events["WORK AND EVENTS<br>every command appends one ledger line, stamped with the<br>episode that wrote it. A line with no episode belongs to<br>none: work outside a session is attributed to nobody<br>rather than to whoever was open nearby."]
  checkpoints["CHECKPOINTS<br>written by hand, and derived on PreCompact — the moment<br>the conversation stops being a place anything is kept.<br>An artefact of the episode: no task required."]
  knowledge["KNOWLEDGE DERIVED<br>on PreCompact and on the close path: the episode's decisions,<br>resolved questions and finished tasks become candidate<br>records from the ledger and git, never from the conversation.<br>Idempotent; the ledger says it ran. [ADR 0091]"]
  closing["CLOSE AND HANDOVER<br>SessionEnd closes the episode and, when work is still open,<br>writes the continuation record. The envelope's reference<br>lists are computed from the ledger at close, never<br>accumulated during the episode."]
  record[".ai/repo/sessions/&lt;stamp&gt;--&lt;episode&gt;--&lt;branch&gt;--&lt;head&gt;--&lt;digest&gt;.md<br>and .ai/repo/knowledge/candidates/&lt;episode&gt;-&lt;digest&gt;.md<br>TRACKED. What survives a clone: everything under .ai/local/<br>names this machine and travels nowhere."]
  resume["A FUTURE RESUME<br>locally, the next episode's briefing resolves the records of<br>this worktree and branch; elsewhere, a clone receives the<br>closed records and reads them as history, because a record<br>is evidence and never authority."]
  watching["and, beside the path, watching it:<br>lifecycle.episodes · lifecycle.recovery · lifecycle.runtime<br>lifecycle.providers · lifecycle.closed · continuity.state<br>projected together on the Cockpit's /cockpit/continuity"]

  cmd & conv --> entry
  entry --> runtime --> client --> episode --> fresh --> context
  context --> events --> checkpoints --> knowledge --> closing --> record --> resume

Dvě políčka na té cestě jsou označena. Znalosti odvozeny je nejnovější: do ADR 0091 byla rozhodnutí epizody, vyřešené otázky a dokončené úlohy důkazem na jednom počítači, pod stropem retence, a nic je nezapisovalo tam, kde by je klon mohl číst. Čerstvost ověřena je to, bez čeho se tento repozitář naposledy obešel: je to krok, ne vlastnost záznamu. Bez něj byla předávka, která byla advanced — pravdivé tvrzení o topologii gitu, stejně pravdivé v den zápisu i o měsíc později — citována do každé nové epizody šest dní poté, co byla dokončena práce, kterou popisovala. Rozhodnutí, prahy a štítky jsou z ADR 0052 „Životní cyklus relace patří epizodě, ne úloze“; každé jiné políčko na cestě je chování, které tento strom už má. A políčko sledováno je tam, kde lokální polovina končí: klon dostane uzavřené záznamy a nic jiného, a proto žádný generovaný dokument ani stránka webu nesmí nést briefing, pracovní kontext ani otevřenou epizodu.

Kam životní cyklus ukládá každou část

flowchart TD
  st["majordomus start #quot;&lt;task&gt;#quot; --scope &lt;paths&gt; [--profile &lt;name&gt;]<br>names any prior record for this branch"]
  ctx["majordomus context<br>the briefing, within budget"]
  work["work happens (Majordomus is not involved)"]
  cp["majordomus checkpoint<br>progress, often, short"]
  dec["majordomus decision add<br>what was decided and why"]
  qq["majordomus question add<br>what is unresolved, and it now blocks"]
  ck["majordomus check<br>scope, state, blockers, store integrity"]
  ho["majordomus handover"]
  nxt["the next session runs context"]
  fin["majordomus finish --outcome &lt;...&gt; --verify-command #quot;&lt;cmd&gt;#quot;"]
  hist["majordomus history<br>the lifecycle, reconstructable"]
  kd["majordomus knowledge derive<br>at the close and on compaction, or by hand:<br>the decisions, answers and outcomes as candidate records"]
  kp["majordomus knowledge promote &lt;id&gt; &lt; evidence<br>a person makes a candidate verified"]

  st --> ctx --> work
  work --> cp
  work --> dec
  work --> qq
  work --> ck
  ck -->|"not finished"| ho --> nxt
  ck --> fin --> hist
  dec --> kd
  fin --> kd
  kd --> kp

Napříč stroji

Vše výše žije v .ai/local/state/ jednoho checkoutu, který se nikdy necommituje a jmenuje stroj, na kterém je. Zastavit se na laptopu a pokračovat na stolním počítači proto potřebuje ještě jeden krok: předávka se publikuje — promítne do přenosného, podepsaného záznamu — a druhý stroj ji obnoví, až plán řekl, že to jde. Návrh je ADR 0105; pravidlo je project.session-continuity-is-portable.

flowchart TD
  ho["machine A: majordomus handover"]
  pub["machine A: majordomus-cli continuity publish<br>refused if it carries a secret or a machine path"]
  ref["refs/majordomus/continuity<br>records/&lt;id&gt;.json, signed by A's device key"]
  syncA["machine A: continuity sync<br>fetch, union, push (never forced)"]
  remote["git remote"]
  syncB["machine B: continuity sync"]
  plan["machine B: continuity plan<br>trust, lineage, source compatibility"]
  resume["machine B: continuity resume<br>handover + decisions written into B's state"]
  ctx["machine B: handover --resolve, context, the session briefing"]

  ho --> pub --> ref --> syncA --> remote --> syncB --> plan --> resume --> ctx

Z MacBooku na Mac mini

# MacBook, once: name the device (the key is the mesh's node key, created on first use)
majordomus-cli continuity device --label macbook-pro

# MacBook, when stopping
majordomus handover < note.md            # # Objective / # Current State / # Next Action
majordomus-cli continuity publish --issue '#184'
git push                                  # the source: continuity never moves it
majordomus-cli continuity sync            # the record: fetch, merge, push refs/majordomus/continuity

# Mac mini
git pull
majordomus-cli continuity sync            # fetches what other devices published
majordomus-cli continuity status          # this device, the store, what is resumable
majordomus-cli continuity plan            # writes nothing; says what a resume would do
majordomus-cli continuity resume          # only on a ready plan
majordomus handover --resolve             # the MacBook's handover, as this checkout's record

Při vstupu kontrola session.continuity preflightu (a poslední řádek banneru) oznámí, že čeká obnovitelná předávka — čtená z .ai/local/state/continuity.json, který zapsal poslední sync, nikdy ze vzdáleného úložiště.

Co se hýbe a co ne

StavPřesunyJak
zdroj, commitnutýanogit, člověkem: git push / git pull
zdroj, necommitnutýnezáznam vypisuje změněné cesty a otisk; plán říká source_incomplete
předávka (cíl, stav, další akce)anohandover záznamu, HandoverBody meshe
záměr: název úlohy, rozsah, profilanotask záznamu; plán doporučuje spustit majordomus start
úkol, milníkanopřenáší se po řádku, dokud publikace nepojmenuje jiné
rozhodnutí, která úloha zaznamenalaanopřipojeno do přijímajícího logu rozhodnutí jednou, označeno Carried:
řada a původanoparent, line, zařízení, epizoda, commit, published_at, verze producenta
cesta worktree, id repozitáře, které shell porovnávánepřepočítáno přijímajícím checkoutem
epizoda, pracovní kontextneotevřeno a zkompilováno na přijímajícím stroji
PID, sockety, porty, lease, výstup buildunezáznam pro ně nemá pole
přihlašovací údaje, tajné hodnoty prostředí, domovské cestynepublikace, která jednu nese, je odmítnuta

Řada a co je konflikt

Záznam uvádí záznam, v němž pokračuje — poslední, který tento checkout zveřejnil nebo obnovil na téže větvi — a id prvního záznamu je jeho řada. Dva záznamy jsou:

VztahVýznamCo se stane
equaltentýž záznamdruhé obnovení nic nepřepisuje
newer / olderjedno sestupuje z druhéhonabídne se novější
divergedstejná linie, ani jedna nepochází z druhé: tatáž práce pokračovaná dvakrátplán, který by jeden vybral, je conflict; člověk jej pojmenuje pomocí --record
independentrůzné řádky: samostatná práceběžné; nikdy konflikt

Hodiny nerozhodují nic: published_at se ukazuje a používá pro stáří. sync hlásí každý řádek jako equal, remote_newer, local_newer, diverged, local_only nebo remote_only.

Plán

continuity plan (táž hodnota přes MCP jako majordomus_continuity_plan a přes HTTP jako GET /api/v1/continuity/plan) odpovídá ready, ready_with_warnings, requires_source_update, conflict, choose_record, nothing_to_resume nebo refused, s:

  • trust — klíč podepisovatele proti trust.allow deklarace meshe; nedůvěryhodný podepisovatel je refused a bez jakékoli deklarace plán varuje origin_undeclared;
  • lineage — line_diverged, would_diverge (tento checkout pokračoval v téže práci jinde), already_resumed;
  • zdroj — exact, local_ahead (varování), local_behind, head_missing, branch_differs (každé requires_source_update, s příkazem gitu ke spuštění), diverged (konflikt: nejprve sesouhlaste s gitem);
  • strom původu — source_incomplete, když byl špinavý, pokud tento checkout nedrží právě tutéž nezacommitovanou změnu (otisky se shodují);
  • aktivita — origin_may_be_active, když záznam pochází ze stále otevřené epizody mladší než hodinu. Poradní: nic nezamyká a spadlý notebook nezamyká nic.

Příkazy, které plán uvádí, jsou doporučení. Nic, co záznam říká, se nespouští.

Offline

publish zapíše lokální ref a nikdy se nedotkne sítě, takže funguje v letadle. sync proti nedostupnému remote skončí exit 10, nic nezmění a zaznamená unreachable do continuity.json; status hlásí úložiště jako pending (nebo never_synced), dokud sync remote nedosáhne. Cestou se nic neztratí.

Bezpečnost

  • Záznam je podepsán mesh klíčem zařízení a identifikován digestem svého obsahu; pozměněný záznam už neodpovídá svému id ani podpisu a je při každém čtení odmítnut.
  • Záznam z jiného repozitáře — fork sdílí jméno, ne kořenovou historii — se odmítne a synchronizace s takovým remote nenechá v stromu tohoto úložiště žádný z jeho záznamů.
  • Záznam pozdějšího schématu se uchová a ohlásí (schema_too_new, upgrade_required), takže starší spustitelný soubor nikdy nezahodí záznamy novějšího.
  • Ref continuity se pushuje na kterýkoli remote, který se synchronizuje: ve veřejném repozitáři jsou jeho záznamy veřejné, stejně jako sledované obálky relací. Tam, kde to není přijatelné, synchronizujte s privátním remote (--remote).

Řešení problémů

PříznakZeptat seObvykle
na druhém stroji nelze nic obnovitcontinuity statusna jedné straně nesesynchronizováno; store.sync říká pending nebo never_synced
head_missingcontinuity planzdroj nebyl pushnut nebo nebyl fetchnut
origin_untrustedcontinuity device na originujeho public_key není v trust.allow
conflict s line_divergedcontinuity recordsstejná práce pokračovala na dvou strojích; jednu obnovte pomocí --record
odmítnutá publikacepole odmítnutítoken nebo domovská cesta v těle předávky nebo v názvu úlohy
continuity.dangling_parentcontinuity synczáznam navazuje na jiný, který toto úložiště ještě nenačetlo

Co to nedělá

Neví, kdo napsal který commit. Úloha zaznamenává commit, na kterém začala, a check považuje každý od té doby změněný soubor za práci úlohy. Když jiná relace commituje na stejnou větev ve stejném checkoutu, soubory té relace se hlásí mimo váš rozsah. Report je správný ohledně souborů a špatný ohledně autora a Majordomus nemá jak rozdíl poznat. Úlohu uzavřete předávkou a začněte novou na aktuálním commitu.

Otázka blokuje celou svou větev, ne úlohu, která se ptala. To je záměr a v jednom případě je to špatně: otázka k opuštěné práci, ponechaná bez odpovědi, odmítá dokončení každé další úlohy na té větvi, dokud někdo nenapíše odpověď. Směr byl zvolen proto, že selhává hlasitě — question list ji pojmenuje a question resolve ji jedním příkazem zruší — zatímco alternativy selhávají potichu. Zamítnuté alternativy a případ, v němž každá chybuje, viz M001 v .ai/repo/project/.

Neměří tokeny, úspory kontextu ani náklady. Rozpočet jsou řádky, protože řádky umí počítat. Viz ECONOMICS.md.

Neřadí výsledky vyhledávání, nic nevkládá do embeddingů a nespravuje index. search je doslovný grep napříč druhy záznamů v pořadí autority. Korpus je hrstka souborů; index by byl druhým zdrojem pravdy, který je třeba udržovat v souladu s prvním.

Neřeší se napříč worktree ani větvemi a nikdy to neudělá mlčky. Předávka z jiného stroje dorazí do tohoto checkoutu jen přes continuity resume, po plánu.

CONCEPTS.md pro slovník · CLI.md pro každý příkaz · SCHEMAS.md pro formáty souborů · KNOWLEDGE.md pro to, co se epizoda naučila a jak se to odvozuje · DESIGN.md pro to, proč mají modely takový tvar.

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.