38. Scénář deklaruje, kde běží, a povinnost je krok
Kontext
Opakovaný návrh, který znovu dorazil v designové poznámce 2026-09-10, je, že .ai/ by měl získat typovanou orchestrační vrstvu: objekty workflow skládající schopnosti do validovaného DAG, aby session-start, fix-bug, merge-ready a jejich příbuzní přestali být prózou, kterou si má agent pamatovat, a stali se něčím, co stroj umí spustit a odmítnout. Motivace je správná a repozitář s většinou souhlasí. Co požaduje, většinou existuje, a část, která ne, je menší než návrh.
Co existuje. context a hook SessionStart jsou session-start. finish se svým kontraktem, handover a kontrolní bod PreCompact jsou session-finish. continuity.md je resume-work. doctor a watch jsou repo-doctor. usecase impact je impact-analysis a selektor, který by test-affected potřeboval. Registr schopností s capability/closure.rs je surface-sync. src/execution/ je smyčka běhu a ADR 33 již rozhodlo, že provedení je sledované volání schopnosti, nikoli druhý registr. share/obligations.yaml s evidence a majordomus.obligation-closure je kontrolní seznam merge-ready, vyhodnocovaný živě na HEAD, kde to git dokáže rozhodnout.
Tři důvody, proč nelze návrh vzít tak, jak je napsán. Zaprvé, workflow zde není volné podstatné jméno: už znamená prózu kontextových dokumentů pod .ai/repo/workflows/ a už znamená just — Surface::Workflow, projections.workflow, identitu workflow.site-build z ADR 27. Třetí význam je to, co project.no-new-nouns odmítá. Zadruhé, workflow engine s vlastními definicemi kroků, schématy a tabulkou tras je druhý subsystém, který ADR 33 odmítlo jménem. Zatřetí, obecný DAG s inferencí, opakováním, rollbackem a kompenzací je to, co project.optional-complexity odmítá jednou větou: a mechanism that exists to define mechanisms ... is the failure this rule refuses.
Co skutečně chybí je užší a je vidět ve výstupu kontroly každé relace. check vyhodnocuje závazky, které aktivní úloha deklarovala v start. Worker, který je zapomněl deklarovat, se to dozví the task declares no obligations a projde. Nic neříká, co pro třídu práce ta třída dluží — že oprava vady dluží test a deklarovanou invariantu, ať už to někdo slíbil, nebo ne. A scénář, jediný spustitelný tvar, který vrstva už má, může běžet jen proti jednorázové fixture; nemůže se zeptat na repozitář, ve kterém worker právě stojí.
Rozhodnutí
Žádný nový druh a žádný engine. Scénář získává místo, kde běžet, a druhou třídu kroku.
-
scenario.mode:fixture(výchozí) nebolive.fixtureje dnes beze změny: setup skript, jednorázový repozitář, commitnuté důkazy.liveběží proti repozitáři, v němž byl příkaz vyvolán, nemá žádný setup a nic nevytváří. -
Živý scénář smí spouštět jen příkazy pouze pro čtení. Třída se čte z
share/commands.yaml, který ji už deklaruje pro každý příkaz a je už autoritou, z níž vychází referenční sémantika;usecase validateodmítne živý krok, jehož příkaz neníclass: read-only, exit 10. Bezpečnost je vlastností deklarace, ne pečlivosti autora. Živý scénář je tedy otázka o tomto repozitáři, nikdy akce na něm: mutace zůstává ustart,checkafinish, které ji řídí, a druhá nedohlížená cesta ke stejným zápisům je přesně to, co toto rozhodnutí odmítá otevřít. -
Krok je buď příkaz, nebo závazek. Krok závazku nese
obligation: <token>zshare/obligations.yamla žádnérun:. Neprovádí se; tvrdí, že závazek je splněn, toutéž cestoulib/evidence.sh, kterou používácheck— zjištěno živě tam, kde to git nebo web dokáže rozhodnout, a čteno z ledgeru tam, kde ne, se stejným posouzením zastaralé versus staré. Vyřeší se napass,unmetnebostale. Kroky závazků jsou platné jen v režimulive: fixture nemá úlohu, která by cokoli dlužila. -
Verdikt živého scénáře je
unmet, nefail. Selhávající fixture scénář je vada nástroje. Živý scénář, jehož povinnost není splněna, je nedokončená práce, a obojí se nesmí hlásit stejným slovem. Runner skončí s 10 — kontrakt nesplněn, kód, který nástroj pro to už používá — nikdy s 1. -
Živé důkazy se nikdy necommitují. Fixture důkazy zůstávají pod
.ai/local/evidence/use-cases/<id>.jsona zůstávají odvozeným, commitnutým artefaktem. Živý výsledek závisí na stroji, stromu a minutě a generovaný soubor, jehož obsah závisí na tom, kdo ho generoval, je jediné, co odvození nesmí vytvořit (ADR 5 a důvod, pročmj_uc_run_onepřipíná název větve fixture). Živé důkazy se zapisují pod.ai/local/evidence/live/a jsou ignorovány. -
usecase runstále ve výchozím stavu spouští fixtures. Živé scénáře se vybírají pomocí--livenebo pojmenováním jejich id, takže význam CI se pod ním nemění.usecase coveragepočítá živý scénář jakopartial, nikdy necovered: záruka, kterou CI nemůže reprodukovat, není záruka.
To skládá, ne centralizuje, což je tvar, který zvolilo ADR 27 pro příkazy a ADR 33 pro provádění. Živý scénář deklaruje uspořádanou množinu otázek a závazků; na každou z nich odpovídá schopnost, která už existuje, a scénář neimplementuje žádnou z nich.
Zamítnuté alternativy
Druh workflow pod .ai/repo/workflows/ s vlastním schématem a runnerem. Návrh tak, jak byl napsán. Přidává třetí význam obsazenému podstatnému jménu, druhou deklaraci „uspořádaného seznamu příkazů s očekáváními“ vedle scénáře a druhý runner vedle usecase run a src/execution/. Každý argument, který ADR 33 uvedlo proti registru akcí, platí beze změny a drift by začal první úpravou, která aktualizuje jen jeden ze dvou.
Nechat živý scénář mutovat, hlídaný nárokem na rozsah úlohy. Výrazně expresivnější a umožnilo by to, aby feature-start byl scénářem od začátku do konce. Vytváří to však také druhý způsob zápisu do dozorovaného repozitáře, před nímž nestojí žádný profil, kontrola rozsahu ani kontrakt dokončení. Mutující polovina feature-start už vlastníka má — worktree create, start a .ai/repo/workflows/task-lifecycle.md — a co jí chybělo, nikdy nebyla mutace. Znovu zvážit jen s konkrétním případem, který polovina pouze pro čtení plus závazky nedokáže vyjádřit.
Nový slovník závazků pro scénáře. share/obligations.yaml už nese tokeny, vstupy, nad nimiž se každý hashuje, a které z nich git rozhodne bez dotazu. Slovník místní pro scénář by byl druhým seznamem, který je třeba udržovat v souladu, a doctor už stávající slovník dokazuje v obou směrech.
Přidat logiku established_by do runneru scénářů. Runner se ptá lib/evidence.sh a hlásí, co řekne. Povinnost, která je živě rozhodnuta na HEAD, je rozhodnuta stejně pro check, pro finish i pro scénář, jinak se tři neshodnou, zda je tentýž strom dokončen.
Důsledky
- Třída práce nyní může říct, co dluží, nezávisle na tom, co si worker pamatoval deklarovat v
start.checkzůstává verdiktem samotné úlohy; živý scénář je verdiktem třídy a obojí se čte společně. usecase validatezískává dvě odmítnutí — živý krok, který není jen pro čtení, a krok povinnosti mimo živý režim — a obě se rozhodují z deklarací, které už existují. Nic nového se neudržuje ručně.- Schéma scénáře se nyní čte na dvou místech se dvěma významy pro
steps. Cenou je jedna větev vmj_uc_run_onea jedna ve validátoru; alternativou byl druhý formát souboru, druhý parser a druhý runner. - Čísla
usecase coveragese nehýbou: živé scénáře nemohou zvýšit cíl nacovered. Živý scénář, který je náhodou jedinou věcí jmenující příkaz, nechává tento příkazpartial, což je poctivé čtení. - Co toto nedodává:
review-changea jakýkoli plán, jehož kroky musí mutovat. Obojí zůstává otevřené a toto rozhodnutí neblokuje ani jedno.