Co-op protokol v1 — více instancí Claude Code nad jedním repem
Operační protokol pro paralelní práci více instancí Claude Code (nebo lidí) na tomto repu. Je čistě provozní: nikdy nepřebíjí editorská pravidla, autorizační log ani build gate v AGENTS.md — ta platí pro každý task beze změny.
Role
- ORCH (orchestrátor) — právě jedna instance, běží v hlavním checkoutu (
/Users/korczis/dev/vomaste.cz, větevmaster). Jediná smí: rozkládat zadání na tasky, editovatdocs/coop/TASKS.md, mergovat domastera pushovat (= deploy). - W-n (worker) — libovolný počet instancí, každá v vlastním git worktree na vlastní větvi, pracuje vždy právě na jednom tasku.
- REV (reviewer, volitelný) — čte diff cizí větve, hlásí nálezy po sběrnici; nikdy needituje.
Identita instance: proměnná COOP_AGENT_ID (např. ORCH, W-1); bez ní se použije jméno aktuální větve.
Rozklad zadání (vstupní prompt → tasky)
Když přijde větší zadání (pastnutý text/prompt), ORCH ho nejdřív rozloží na tasky a zapíše do boardu — teprve pak se začne pracovat:
- Rozsekat na nezávislé, mergovatelné jednotky (jedna jednotka = jde samostatně zvalidovat přes
npm run builda samostatně mergnout). - Každé jednotce dát ID
T-###, titul, přesný scope (které soubory / sekce), akceptační kritérium a závislosti na jiných taskách. - Cokoliv, co by se dotklo obsahu o reálné osobě, dostane štítek
[scope-check]a před startem se ověří proti autorizačnímu logu vAGENTS.md; nový subjekt/téma = stop a zeptat se vlastníka. - Board commitnout na
master— tím je zadání trackovatelné v gitu.
Task board — docs/coop/TASKS.md
Jediný zdroj pravdy o stavu práce, single-writer: edituje ho pouze ORCH, pouze na master. Workeři stav nikdy needitují — hlásí ho po sběrnici a ORCH ho propíše. (Stejný vzor jako single-source-of-truth u dossieru: jedna kanonická reprezentace, žádné souběžné zápisy.)
Stavy: todo → claimed → in-progress → review → merged, kdykoliv blocked (s důvodem v poznámce).
Worktrees — jeden task = jedna větev = jeden worktree = jedna instance
Worktrees žijí mimo repo v ~/dev/vomaste-worktrees/ (zavedený precedens), větve se jmenují task/T-###:
scripts/coop/coop.sh wt-add T-001 # git worktree add ../vomaste-worktrees/T-001 -b task/T-001 master
cd ~/dev/vomaste-worktrees/T-001
npm ci # worktree má vlastní node_modules
npm run generate:all # generované vstupy; bez nich padne pre-commit i build
COOP_AGENT_ID=W-1 claude # nová instance Claude Code v worktree
Ten druhý příkaz nevynechávej. data/generated/ je gitignorované, takže v čerstvém worktree neexistuje — a každá brána, která ten výstup čte, spadne dřív, než se stihne kdokoli podivit. Bez npm ci padne hned první kontrola (data:validate) na chybějícím ajv. S ním, ale bez generátorů, padne verify:source-catalog v pre-commitu a test katalogu pravidel uvnitř npm run build. Žádná z těch hlášek neřekne „neběžely generátory“ — všechny čtou jako rozbitá data.
Původní znění tady radilo jen npm run build:source-catalog. Bylo to přesné pro pre-commit a nepřesné pro npm run build, a autor téhle věty na tu neúplnost narazil do hodiny od chvíle, kdy ji napsal. Odtud i obecné pravidlo: kontrola nad generovaným, gitignorovaným artefaktem musí běžet až za svým generátorem. Zatím to nic nevynucuje a stálo to 2026-08-13 dvě červené produkce; kdo na to sáhne příště, ať to zváží jako bránu.
Po mergnutí: scripts/coop/coop.sh wt-done T-001 (odstraní worktree i větev). Worktree se nikdy nerecykluje na jiný task.
Riziko hlavního checkoutu: rozdělaná práce může beze stopy zmizet
Pozorováno živě 2026-08-06: rozdělané (ne ještě commitnuté) úpravy dvou souborů v hlavním checkoutu (/Users/korczis/dev/vomaste.cz) během několika minut úplně zmizely — git status najednou hlásil čistý strom přesně odpovídající HEAD, ve chvíli, kdy se tam objevil nový, nesouvisející commit z jiné souběžné session. Přesný příkaz, který k tomu vedl, nebyl zjištěn (žádost o git reflog/historii shellu jiné instance není k dispozici) — ale výsledek odpovídá tomu, co udělá git checkout -- <soubor>, git reset --hard nebo git stash bez následného pop spuštěný přímo v tomhle checkoutu, zatímco v něm leží něčí neuncommitnutá práce. Na rozdíl od konfliktů popsaných výš (rebase/merge/build) tohle není konflikt, který by cokoliv nahlásilo — žádná chybová hláška, žádný build fail, jen tichá ztráta.
Zmírnění, dokud hlavní checkout sdílí víc než jedna instance zároveň:
- Necommitnutou práci tu nenechávej ležet dlouho. Malý, rychle commitnutý krok přežije; hodina rozpracovaných úprav ve working tree je vystavená riziko celou tu hodinu.
- Před jakýmkoliv
git checkout/reset/stash/cleanv hlavním checkoutu zkontrolujgit statusa zvaž, jestli by ses tou operací nepřehnal přes cizí rozdělanou práci — stejné pravidlo, jaké tenhle agent (Claude Code) dostává jako obecnou instrukci, platí tu zdvojnásobeně, protože „cizí" tu může být jiná souběžná instance, ne jen historie z minula. - Pokud potřebuješ delší rozpracovanou úpravu, zvaž dočasný worktree i pro práci, která by jinak šla přímo do hlavního checkoutu jako ORCH (viz sekce výš) — je to jediná záruka, kterou git nabízí proti přesně tomuhle.
- Ztracenou práci lze často rekonstruovat z konverzačního kontextu agenta, který ji psal (přesně tak se to řešilo 2026-08-06) — ale to je záchranná síť, ne omluva to riskovat znovu.
Sběrnice zpráv (serializace)
Instance spolu mluví přes append-only NDJSON log ve sdíleném git adresáři — ten je společný všem worktrees a není verzovaný:
$(git rev-parse --git-common-dir)/coop-bus/bus.ndjson
Jedna zpráva = jeden řádek = jeden JSON objekt (řádky < 4 kB jsou na POSIX appendované atomicky, takže netřeba zámky):
{"v":1,"ts":"2026-07-29T19:00:00Z","from":"W-1","to":"ORCH","type":"progress","task":"T-001","payload":{"note":"claims table hotova, bezi build"}}
v— verze protokolu (teď1)from/to— ID instance;to:"*"= broadcasttype—claim|progress|blocked|review-request|done|merged|deploy|note|pingtask—T-###, nebo""u zpráv mimo taskpayload— libovolný validní JSON objekt (typicky{"note": "..."})
Obsluha přes helper (nikdy neručně, ať je formát konzistentní):
scripts/coop/coop.sh send ORCH claim T-001 '{"note":"beru si to"}'
scripts/coop/coop.sh inbox # zprávy pro mě (a broadcasty)
scripts/coop/coop.sh log 50 # posledních 50 zpráv celé sběrnice
scripts/coop/coop.sh status # board + worktrees + poslední zprávy
Instance čtou sběrnici na začátku session (SessionStart hook pouští coop.sh status) a průběžně mezi kroky — žádný push kanál není, je to poll.
Životní cyklus tasku
- ORCH zapíše task do boardu (
todo) a pošlenote/broadcast. - Worker pošle
claim→ ORCH propíšeclaimed+Owner. - Worker pracuje ve svém worktree, průběžně posílá
progress; commituje malé, atomické commity natask/T-###. - Hotovo = ve worktree prošel celý
npm run build→ worker pošlereview-request(payload: větev, shrnutí, výstup buildu OK). - ORCH (příp. REV) zkontroluje diff, mergne do
master(git merge --no-ff task/T-###), pustínpm run buildještě jednou na masteru, propíšemergeddo boardu. - Deploy =
git pushmasteru (GitHub Pages CI v.github/workflows/deploy.ymlstaví a nasazuje). Od 2026-08-05 se tohle po commitu namasterděje automaticky — viz „Automatický push po commitu" níž — ORCH ho tedy typicky nespouští ručně, jen sleduje, že sedeployzpráva objevila na sběrnici. Průběžně: deploy po každém mergnutém tasku, ne až nakonec. wt-done T-###uklidí worktree a větev.
Konflikty řeší vždy worker rebasem své větve na aktuální master (git fetch && git rebase master) — ORCH nikdy neřeší konflikt za něj při mergi.
Automatický push po commitu a mergi (post-commit + post-merge hooky)
.githooks/post-commit a .githooks/post-merge (stejný instalační mechanismus jako .githooks/pre-commit — core.hooksPath, viz README „Rychlý start") od 2026-08-05 dělají na master automaticky přesně tu sekvenci, kterou by jinak ORCH spouštěl ručně, po každém commitu i po každém mergi:
fetch origin master → rebase → npm run build (CELÝ, ne jen pre-commit
podmnožina) → push origin master → coop.sh send "*" deploy …
Vzniklo ze session 2026-08-05, kde tenhle postup ORCH opakovaně spouštěl ručně po každém commitu (viz git historie kolem AUTH-2026-08-05 rozšíření james-quick dossieru).
Bezpečnostní chování, které z toho dělá něco jiného než „vždy pushni":
- Spustí se jen na
master— workeři v task worktreech (task/T-###) jím nejsou dotčeni, single-writer pravidlo výše platí beze změny. - Nikdy se nespustí uprostřed rebase/cherry-picku/bisectu ani nad rozpracovaným řešením konfliktu (jinak by se spouštěl na každém mezikroku
git rebase --continuea pushoval rozpracovaný stav). Když se z tohohle důvodu nespustí, napíše to — do 2026-08-13 mlčel a „nic se nevypsalo" se čte jako „nasadilo se to". - Dokončený merge mezi ně nepatří — a do 2026-08-13 patřil. Guard bral přítomnost
MERGE_HEADjako „merge probíhá", jenže git ho po úspěšnémgit merge --no-ffuklízí až po doběhnutí post-merge hooku. Hook se tedy umlčoval přesně pro ten případ, kvůli kterému vznikl: merge worker větve namasterse tvářil jako nasazený, ačkoli push nikdy neproběhl a musel se dodělat ručně. Hlídá tonpm run test:hooks(součástbuildicheck) — a hlídá to fixturem, ne odvozením z kódu, protože chybné rozhodnutí tohohle guardu znamená buď tichý nedeploy, nebo zveřejnění, které nikdo nechtěl. - Rebase na konflikt se vzdá (
rebase --abort) a nechá commit lokální — žádné automatické řešení konfliktů. Recept na typicky konfliktní generované soubory je v sekci níž. - Před pushem musí projít celý
npm run build, ne jen rychlá pre-commit podmnožina — červený build se nikdy nepushuje, protože push namasterje live deploy. Commit zůstává lokálně, jen nejde ven automaticky; oprav a commitni znovu. - Až 3 pokusy fetch+rebase+build+push (řeší prohraný závod se souběžným pushem odjinud), pak se vzdá se srozumitelnou hláškou.
- Únik:
COOP_NO_AUTOPUSH=1 git commit …/COOP_NO_AUTOPUSH=1 git merge …(např. vědomě rozpracovaný stav, který se ještě nemá dostat ven) — pak platí stará ruční sekvence z kroku 6 výše.
Tohle nenahrazuje krok 4/5 (worker → review-request → ORCH merguje --no-ff do masteru) — ten merge commit na masteru je to, co hook následně automaticky pushne. Automatizuje se jen „a teď to dostaň ven", ne rozhodnutí, jestli se má mergnout.
Proč dva hooky, ne jeden: objeveno živě 2026-08-06 — git merge (a git pull, což je fetch+merge) spouští jiný pár hooků, pre-merge-commit/post-merge, NIKDY pre-commit/post-commit. Dokud existoval jen .githooks/post-commit, přesně krok 5 výš (git merge --no-ff task/T-###) auto-push úplně obcházel — každý coop merge se musel pushovat ručně, což je přesně ten manuální krok, který měl tenhle mechanismus odstranit. Sdílená logika (guardy, fetch/rebase/build/push smyčka) teď žije v .githooks/lib/auto-push-master.sh, které volají oba hooky se svým vlastním jménem (jen pro log prefix — chování je shodné). Regresní test: scripts/build/git-hooks.test.mjs.
Konflikty na generovaných/derivovaných souborech
Session 2026-08-05 (souběžné rozšiřování james-quick dossieru ve dvou instancích) ukázala, že nejčastější rebase konflikt při aktivním koop provozu není v ručně psaných kanonických datech, ale v souborech, které si dvě instance nezávisle přegenerovaly ze stejné výchozí verze. Recept je pro každý typ jiný, ale vždy mechanický — žádná ruční aritmetika:
scripts/data/compiled-golden.snapshot.json(počty záznamů pro golden test) — vezmi libovolnou stranu konfliktu (git checkout --oursnebo--theirs), paknpm run test:update-golden. Nikdy needitujcompiled-golden.test.mjsani JSON ručně — proto byl po téhle session refaktorovaný z hardcoded literálu právě do přegenerovatelného JSON souboru.data/discovery-log.jsonl(append-only NDJSON) — konflikt řeš jako sloučení obou stran (ponech obě sekvence řádků, žádnou nezahazuj), nikdy needituj počty ručně. Po dořešení ostatních konfliktůnpm run data:buildověří, že log dál sedí s daty.reports/*.md,data/generated/routes.json,data/generated/navigation.json— čistě generované, nikdy je needituj ručně. Na konfliktu vezmi libovolnou stranu (git checkout --theirs <soubor>) a spusťnpm run build(routes a navigation) nebo aspoňnpm run data:build(reports) — přegenerují se z aktuálních dat, cokoliv jsi vzal jako výchozí obsah je jedno.- Past:
data/generated/**je v.gitignore, ale pogit reset --hard/rebase přežívá na disku ze staré verze adata:check-generated:contentpak hlásí zdánlivě nesmyslné route parity chyby (routa pro smazaný záznam pořád vroutes.json, nová routa v něm chybí). Fix:npm run build:routes(abuild:navigation) před dalším krokem, ne ladění obsahu — soubor je jen zastaralý, ne rozbitý. - Past, druhá a horší varianta: dva plné
npm run buildběhy ve stejném checkoutu (ne dva různé worktrees — to je bezpečné, každý má vlastnídata/generated/) se dřív mohly přetahovat odata/generated/views/**už v krocíchdata:views/data:generate-content/data:sync-content, dřív než došly nazola build, kde je chránilwith-build-lock.mjs. Příznak:zola buildspadne naload_data: .../clm-NN.json doesn't exist, přestože ten soubor existuje — vypadá to jako datová chyba, není. Reprodukováno živě 2026-08-05/06: dvě souběžné session ve stejném checkoutu, každá narazila na jiný chybějící view model, a soubor pak vždy existoval. Od 2026-08-06pipeline.mjszamyká celýbuildrežim (oddata:viewspoverify:export), ne jen zola krok — viz komentář vwith-build-lock.mjs..githooks/post-commit(auto push po commitu, viz níž) tenhle risk zvyšuje, protože teď plný build spouští automaticky každý commit na masteru, ne jen člověk, když si vzpomene.
ID kolize u souběžně rozšiřovaného dossieru
Když dvě instance nezávisle přidávají nové CLM-##/SRC-##/… záznamy do téhož dossieru, obě si typicky spočítají stejné „další volné" číslo z toho, co vidí lokálně — a dorazí ke stejnému ID s jiným obsahem (add/add konflikt, který git nedokáže automaticky sloučit). Před přidáním nového záznamu do dossieru, na kterém může souběžně pracovat i jiná instance:
git fetch origin mastera zkontroluj aktuální nejvyššíCLM-##/SRC-##/… v cílovém dossieru na originu, ne jen lokálně — lokální stav může být starší. Od 2026-08-06 to dělá jeden příkaz:npm run dossier:next-id -- --dossier=<slug> --registry=<claims|sources|cases|gaps>— fetchne, zkontroluje oboje a nahlas se rozkřičí, pokud se lokál s originem liší (vizscripts/dossier/next-id.mjs, vzniklo přesně z tohohle bodu poté, co „zapomněl jsem fetchnout" byla skutečná příčina kolize níž).- Pokud i tak dojde ke kolizi (viděno 2026-08-05: dvě session obě sáhly po
CLM-09/SRC-09/SRC-10ve stejném dossieru), neřeš to přepisem cizích záznamů. Přečísluj svoje nové záznamy tak, aby navazovaly za tím, co je skutečně na originu (ne co bylo v tvé poslední lokální kopii), včetně všech míst, která ID nesou:identifier,@id,title, cross-reference pole (sources/claimsv obou směrech),order, hand-authored tabulka a graf vdossier.json,content/markdown (ten se ale nikdy needituje ručně — po přečíslování kanonických JSON stačínpm run data:build).npm run data:validatepo přečíslování potvrdí, že nezůstal viset žádný neplatný odkaz.
Paralelismus uvnitř jedné instance
Souběžné instance přes worktrees jsou pro nezávislé, dlouhoběžící tasky. Pro fan-out uvnitř jednoho tasku (průzkum, review z více úhlů) má každá instance k dispozici vlastní subagenty (Agent tool / Workflow) — ty koordinuje sama a na sběrnici hlásí jen souhrnný stav tasku. Žádný vestavěný „co-op mode" v Claude Code není; co-op je přesně tento protokol.
Vazba na pravidla repa (závazné)
- Editorská pravidla, autorizační log a build gate z
AGENTS.mdplatí v každém worktree stejně jako na masteru. Sběrnice ani board nikdy neslouží k obcházení „stop and ask" u obsahu o reálných osobách. - Merge do
masterjen se zelenýmnpm run build. Červený build na masteru = stop-the-line: žádné další merge, dokud není zelený. - Board i protokol jsou verzované v repu; sběrnice (
coop-bus/) je efemérní provozní log a do gitu nepatří.