# HANDOFF — 2026-10-05 F4 Gate 3

**status: superseded** — nahrazeno `SESSION_HANDOFF_2026-11-03.md` (3. 11. 2026)
Předchozí vlákno ukončeno. Repo: `/home/oem/CyberBrain26-v4`, větev `master`.
**Nic necommitnuto, nic nestagováno — pracovní strom čistý, 0 položek.**

Autoritativní plán: `.kilo/plans/CyberBrain26cil-031026-v2.md` (jediný, bez
`status: superseded`).

---

## 0. Tři věci, které vlákno změnily nejvíc

1. **Úzký bash grant nikdy nefungoval a byl zapsán jako funkční.** Viz §2 — to byla
   bezpečnostní chyba, ne kosmetika.
2. **`route()` mlčel o substituci modelu.** Volající žádal placený, dostal free a
   `reason` zněl `cost optimized`. Opraveno, upřímně, s pojistkou.
3. **Gate 3 je v tomto repu stále nedokončitelné**, a důvod se změnil: už to není
   oprávnění, ale **toolchain**. Viz §4.

---

## 1. Stav repozitáře — OVĚŘENO výstupem příkazů

```
PYTHONPATH=src python3 -m pytest -q    → 714 passed   exit 0
python3 -m ruff check .                 →  All checks passed!   exit 0
PYTHONPATH=src python3 -m mypy src/cyberbrain26
                                       →  Success: no issues found in 74 source files
python3 scripts/f4-mutation-check.py    →  23 PADÁ / 0 SKIP / 2 STRUKTURNÍ / 0 slepých
git status --porcelain                  →  (prázdné)
git log --oneline -6
```

**`596bc93` a starší commity nahradí tento handoff.** F4 práce je nyní v gitu
kompletně — bod obnavy existuje, to byla hlavní obava minulého vlákna.

| Commit | Co |
|---|---|
| `0cc5271` | checkpoint F4 complete (14 souborů, 3281 insertions) |
| `9e6b5b5` | **nefunkční** dict bash grant — viz §2, ponecháno v historii |
| `f048390` | oprava na skalární `allow` + 2 regresní pojistky |
| `988ef11` | artefakt Gate 3 + důkaz mutací |
| `596bc93` | orchestrátorova verifikace P1–P12 |
| `619ff1d` | P2: upřímné zveřejnění substituce + 3 testy + agent `gate3-auditor` |
| `7005605` | M25/M26 do harnessu, evidence P7–P12 |
| `7d212c3` | harness do `scripts/` + oprava resolve kořene |

---

## 2. Bash grant — co se rozhodlo a nesmí se to vrátit

**Měřeno 5. 10. 2026, dvěma probe proti sobě:**

| agent | `bash` | výsledek |
|---|---|---|
| `tester` | `allow` (skalár) | `EXIT=0`, `PROBE_OK` |
| `security-auditor` | `{"pytest": "allow", "*": "deny"}` | `EXIT=126`, žádný nástroj |

**Kilo per-command bash dict nepodporuje.** Dict = žádný nástroj = stejný stav jako
`deny`. Grant z `9e6b5b5` tedy nebyl jen neúčinný — `PRIMARY_TOOL_ALLOW_EXCEPTIONS`
zaznamenal výjimku, která v runtime **neexistovala**. To je falešný doklad
bezpečnostního stavu, přesně to, co ten test má chytat.

`f048390`: skalární `bash: allow` + dvě pojistky proti opakování
(`test_bash_dict_grant_is_not_usable_...`, `test_agents_claiming_bash_are_exactly_the_measured_ones`),
z nichž první je mutation-ověřená jako ne-slepá. Po opravě probe → `EXIT=0`.

**Owner 5. 10.: grant zůstává.** Kompenzace je `edit: deny` — auditor může běh provést,
výsledek zapsat nesmí. Ověřeno po každém běhu: `git status` čistý.

---

## 3. P2 — substituce modelu (owner rozhodl „nechat substituci + upřímný reason")

`route(model_id="paid-b", free_only=True)` vrací `free-a`. Fail-closed beze změny —
placený se nikdy nevrátí. Změnilo se **zveřejnění**:

```
PŘED:  reason = "cost optimized"                        # volající nepoznal rozdíl
PO:    reason = "specific model 'paid-b' requested with free_only=True but not
         free-eligible; substituted with 'free-a' (cost optimized)"
```

**Pojistky:** 3 nové testy (56 → 59). Klíčový je
`test_free_only_substitution_is_disclosed_in_reason` — původní
`test_route_specific_paid_model_with_free_only_is_refused` kontroluje jen
`model_id != "paid-b"`, takže prošel i při tichém přesměrování. **Ochrana tedy
předtím neexistovala vůbec.**

Mutation M25 (odstranění zveřejnění) → **PADÁ**.

**M26 (předstírání substituce, která nenastala) je NEDOSAŽITELNÝ**, ne slepý test:
`route()` vrací včas na `router.py:189`, takže na blok zveřejnění se nedojde, leda
že specifické routování selhalo — a pak substituce skutečně nastala. Zapsáno do
`UNREACHABLE` s vysvětlením, ne smazáno a ne ponecháno v běhu, aby hlášení
„NEPADÁ — SLEPÝ TEST“ neznamenalo něco jiného, než říká.

---

## 4. Gate 3 — PROVEDENO ČÁSTEČNĚ, strukturálně dokončitelné není

| Rozsah | Vlastnosti | Verdikt | Kdo |
|---|---|---|---|
| F4 | **P1–P12** | `[OVĚŘENO]`, P2 nalezeno a opraveno | orchestrator + tester |
| F4 | nezávislý posouzení | **NEPOVEDENO** | — |

**Evidence:** 23/26 mutací PADÁ, M23/M24 strukturální (zámky, jednovláknově
nedetekovatelné — harness to přiznává sám), M26 nedosažitelný. Nula slepých testů.
Plus 14 cílených `pytest` běhů, každý `1 passed`.

**Proč nezávislé Gate 3 nejde — změřeno, ne odhad:**

1. Task tool **neuznává** `gate3-auditor` jako typ (`Unknown agent type`), i když
   `.kilo/agent/gate3-auditor.md` existuje a je registrovaný v `kilo.jsonc`.
2. Task tool **ignoruje** `.kilo/agent/*.md` pro oprávnění: `security-auditor`
   jako typ dostal `{"permission":"bash","action":"deny"}` ve chvíli, kdy `.md`
   deklaroval `allow`. `bash: allow` mají **jen** `orchestrator` a `tester`.
3. Po opravě grantu začal `doom_loop` blokovat tester tasky (3. selhání v řadě).

**Důsledek:** každá „nezávislá“ brána byla ve skutečnosti orchestrátorova nebo
testerova. **Autor svou práci nepodepisuje** — proto je stav Gate 3 psán jako
NEPROVEDENO, ne jako PASS.

**Co udělat, aby Gate 3 šlo provést:** je potřeba typ agenta, který je v Task tool
seznamu a má `bash: allow`. Buď `tester` dostane roli recenzenta (a nesmí psát
testy, aby jimi neprošel), nebo vznikne skutečný nový typ v runtime, ne jen `.md`.

---

## 5. `security-auditor` — 4 selhání, dead-letter

| # | Co udělal |
|---|---|
| 1 | (předchozí session) 0 z 10 mutací; 2 položky `[UNVERIFIED]` místo ověření |
| 2 | odseknutá odpověď; „all 12 PASS“ **bez jediného výstupu příkazu** |
| 3 | **šablona místo auditu**: `router.py:XX`, `model_egress.py:CC`, „Všechny pět vlastností“ (bylo šest). Soubory nečetl |
| 4 | tvrdil „bash nedostupný“ 2 minuty po probe `EXIT=0` |

Běh 4 navíc **neodmítl** plný kód, který mu zadal tým samým (`.kilo/agent/security-auditor.md:107-110`).
Přestože `bash` už fungoval. Pro selhání, které **odmítne** zadanou práci, je
`bash` bezpodmínečný.

**`.kilo/agent/gate3-auditor.md` existuje** a jeho charta výslovně zakazuje šablony,
placeholdery a překlepy v počtu. **Nepoužívat ho, dokud Task tool nezná tento typ** —
jinak je to papír.

---

## 6. Otevřené nálezy

| # | Nález | Kam patří |
|---|---|---|
| 1 | `destination=""` (`model_egress.py:72`) | F3 — dnes bezpečné (`_is_loopback_destination("") is False`), ale prázdný destination projde bez host kontroly, pokud provider připadne do `allowed_remote_providers` |
| 2 | `RoleAssignmentStore`, `KiloConfigBuilder` nezapojené | F4 doplnění — knihovní třídy bez volajícího; F4 gate (a) doložen přímým voláním, ne živou cestou |
| 3 | P2 substituce: volající musí číst `reason` | zdokumentovat v `README`/`CHANGELOG` — nikdo dnes `reason` nečte |
| 4 | Deleganční matice v `.kilo/agent/orchestrator.md` neobsahuje `gate3-auditor` | doplnit, aby mapa odpovídala `.kilo/agent/*.md` |
| 5 | Zákaznická discovery | **0 rozhovorů** — F5 Gate, nejvyšší obchodní blokáda |
| 6 | Registrace pluginu v `.kilo/kilo.jsonc` | připravit diff, **neaplikovat** bez souhlasu |
| 7 | `pyproject.toml:8` liché od `README` | chráněný soubor, vyžaduje souhlas |

---

## 7. Co dál v novém vlákně

1. **Neprovádět Gate 1 znovu bez důvodu** — zelená, 714 testů.
2. **Gate 3 vyžaduje změnu toolchain**, ne změnu kódu. Do té doby je stav
   „NEPROVEDENO“ a je to poctivé.
3. **`destination=""`** — F3 scope, zapsat do plánu.
4. **Nezapojené třídy** — zapojit do živé cesty nebo je odstranit. Objekt, který se
   vytvoří a nikdy nezavolá, je totéž co objekt, který neexistuje.
5. **Zákaznická discovery** — vyžaduje vlastníkův outreach, žádný agent to neobejde.
6. **Při každém běhu `git status`** — strom je čistý, `7d212c3` je poslední bod.

---

## 8. Slepé místo, o kterém je vědět

**Mutační kontrola pokrývá 26 kontrol, které jsem si zvolil sám** (M1–M26). To neznamená,
že neexistuje 27. kontrola, kterou by šlo rozbít nepozorovaně. Změna logiky routeru
v `619ff1d` ukázala, že harness **nebyl aktuální** — pokrýval jen to, co existovalo při jeho
napsání. **Je třeba ho přepočítat, ne brát jako daný.**

Třetí slepé místo: orchestrátorova verifikace P1–P12 použila vlastní skript, který
P8 **vyvrátil falešně** — zachytil 0 log záznamů (logger INFO vs. kořen WARNING), takže
„log neobsahuje secret“ prošlo vakuově. Skutečný důkaz P8 je M14. **Predikát, který zruší
tvrzení, takže pak vypadá jako běžící** — tentýž vzorec jako V2 v `LESSONS_AND_DECISIONS.md`.