# HANDOFF — 2026-10-05 01:45 CEST

**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 má 9 položek (§2).

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. **F4 prošla od mrtvého kódu k funkčnímu.** Předchozí implementer ponechal
   všechny 4 soubory neimportovatelné a 3 testovací soubory vůbec nevytvořil,
   přičemž to v reportu neuváděl.
2. **`free_only` v routeru existoval, ale nikam se nepředával.** Bez zásahu by
   F4 gate „bez placeného fallbacku" **neplatila vůbec** — `_get_fallback_candidates()`
   vracelo vše z `list_active()` bez ohledu na `free_only`. Toto je nejvážnější
   nález vlákna.
3. **`security-auditor` nemá `bash`, a proto Gate 3 nemohl dokončit.** Nešlo o
   jeho chybu — struktura zadání to neumožnila. Viz §6.

---

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

```
git rev-parse --abbrev-ref HEAD   → master
git log --oneline -2              → 1f80b29  checkpoint: restore point before autonomous F4 work
                                   4fa4201  docs: handoff for 2026-10-04 21:50 …
git status --porcelain | wc -l    → 9
```

`1f80b29` je **čistý bod obnovy před veškerou prací na F4**. Regresní cesta:

```bash
cd /home/oem/CyberBrain26-v4
git checkout -- .kilo/kilo.jsonc src/cyberbrain26/control_plane/model_gateway/__init__.py src/cyberbrain26/model_layer/router.py
rm -f src/cyberbrain26/model_layer/role_assignment.py \
      src/cyberbrain26/model_layer/kilo_config.py \
      src/cyberbrain26/control_plane/model_gateway/model_egress.py \
      tests/test_f4_model_intelligence.py \
      mutation_check_f4.py
rm -rf .kilo/context/rollback
```

Druhá pojistka (mimo git): `.kilo/context/rollback/f4-20261005-010654/`
— kopie všech 4 souborů ve stavu před opravou, s příponou `.f4-rollback`
(suffix záměrně, aby nepletly ruff).

**NEPOUŠTENO a neuděláno:** žádný commit, žádný stash, žádný push. `AGENTS.md`
to bez výslovné žádosti zakazuje.

---

## 2. Změněné soubory — 9 položek

```
 M .kilo/kilo.jsonc                                        (schválené vlastníkem)
 M src/cyberbrain26/control_plane/model_gateway/__init__.py  +71 -6
 M src/cyberbrain26/model_layer/router.py                   +43
?? src/cyberbrain26/model_layer/role_assignment.py          nový, 138 ř.
?? src/cyberbrain26/model_layer/kilo_config.py              nový, přepsán celý
?? src/cyberbrain26/control_plane/model_gateway/model_egress.py  nový, 127 ř.
?? tests/test_f4_model_intelligence.py                      nový, 40 testů
?? mutation_check_f4.py                                     pracovní nástroj, viz §5
?? .kilo/context/rollback/                                 záloha, viz §1
```

`git diff --stat` (tracked): 110 insertions, 6 deletions ve 3 souborech.

### 2.1 `.kilo/kilo.jsonc:80`

```diff
-    "external_directory": "deny",
+    "external_directory": { "/tmp/kilo/**": "ask", "*": "deny" },
```

Změnil **vlastník ručně** v terminálu 5. 10. 2026. Pattern je první, protože
se `bash`/`external_directory` vyhodnocují **shora dolů a první shoda vyhrává**.
Záloha v `.kilo/kilo.jsonc.bak-extdir`.

`external_directory: deny` s `pattern: *` **blokoval veškerý přístup mimo
workspace** — dvakrát v předchozím vlákně to zastavilo zápis do `/tmp` a přípravu
kontroly záměrného poškození. Nyní je `/tmp/kilo/**` na `ask`, vše ostatní zůstává
`deny`.

### 2.2 `model_layer/router.py` — nejvážnější oprava

`free_only` existoval v signatuře, ale **nikde se nepoužíval**:

| Řádek | Co bylo | Co je |
|---|---|---|
| `_get_candidates` | žádný filtr | `free_only` → filtr `m.is_free` |
| `_get_fallback_candidates` | žádný filtr | `free_only` → filtr `m.is_free` |
| `_route_specific_model` | žádná kontrola | `free_only` → vrátí `None` pro placený |
| `route:190` | `_get_candidates(provider, capabilities)` | předává `free_only` |
| `route_with_fallback:236` | `_get_fallback_candidates(capabilities)` | předává `free_only` |

`free_only=False` zůstává default, takže se **chování pro existujícího volajícího
nemění** — to je důvod, proč spadla jen 1–4 testy na mutaci, ne celá suite.

### 2.3 `model_gateway/__init__.py` — zapojení do živého systému

Nový kód by jinak byl **mrtvý** (`grep` §4.1). Zapojen:

- `ModelGatewayConfig.free_only: bool = True`
- `route()` a `route_with_fallback()` předávají `free_only` do routeru
- `ModelGateway.__init__(config, *, egress_gate=None, agent_id="model-gateway")`
- `chat()` volá `_authorize_egress()` **před** voláním providera
- `_fallback_chat()` autorizuje každý fallback provider zvlášť
- Chybějící klasifikace = `SecurityError`, **ne předpoklad PUBLIC**

### 2.4 Tři nové soubory — co bylo rozbité

Všechny tři **nebyly importovatelné**. `python3 -c "import …"` to potvrdil
před opravou.

`model_layer/role_assignment.py`
- `class ModelRef(\n dataclass\n):` — `dataclass` je decorator, ne base class
- `class RoleModelAssignment(` — nezavřená závorka, rozsypaná definice
- `time.time()` bez `import time`
- `backup.verified` / `backup.is_free` — `ModelRef` tato pole neměl
- `self._lock = None` s komentářem „would be thread-safe in production"
- `assign()` vyžadoval `backups` povinně → `len(None)` v logu

`model_layer/kilo_config.py` — **přepsán celý**, byl bez záchytných sítí
- import `cyberbrain26.plans` — **modul v repu neexistuje**
- `CB26_SCHEMA` použit jako **set element místo klíče slovníku** → SyntaxError
- `logger` použit, nikdy definován
- `backup_kilo_config(model_registry=None)` → AttributeError
- `apply_kilo_config` **nezapisoval nic**, vracel `{"applied": True}` = tichý no-op
- `model.name` / `model.id` — `ModelRecord` tato pole nemá

`model_gateway/model_egress.py` — **bezpečnostní díra**
- `EgressGate` importován, ale **nikdy nezavolán**
- `authorize_model_call` končil na `pass` + komentář *„For now we simulate
  successful authorization"* → autorizace je no-op
- `SecurityError` použit, neimportován
- `*,` dvakrát v signatuře

Nová implementace: `EgressGate.authorize()` se opravdu volá, `classifications`
jsou povinné bez defaultu, `egress_gate is None` → `SecurityError`, surový
payload se nikdy neloguje (jen SHA-256).

---

## 3. Brány

```
pytest -q                          → 691 passed           exit 0
ruff check .                       → All checks passed!   exit 0
mypy src/cyberbrain26              → Success, 74 files    exit 0
pytest tests/test_f4_*.py -q       → 40 passed in 0.37s   exit 0
kilo --version                     → 7.8.3
```

**Gate 1 zelená.** Gate 2 viz §4. **Gate 3 neprovedena** — viz §6.

Při předchozích bězích v repu: 626–650 testů. Nyní 691 = +41 (40 nových F4
+ 1 existující, který se začal pouštět po opravě `external_directory`).

---

## 4. Gate 2 — tři kritéria F4 z plánu §5, VŠECHNA DOLOŽENA

### 4.1 (a) Přiřazení různých modelů dvěma rolím

```
kilo-cli:         main=qwen3-4b           backup=['llama3-8b']
vscodium-editor:  main=qwen3-coder-30b   backup=['llama3-8b']
distinct: ['llama3-8b', 'qwen3-4b', 'qwen3-coder-30b']
výběr při nedostupnosti main: llama3-8b
```

### 4.2 (b) Nedostupnost BEZ placeného fallbacku

```
jen PAID v registru + free_only=True
  route_with_fallback(model_id="__missing__", free_only=True)
    → ModelRouterError: All fallback routes exhausted for the given criteria
  route(free_only=True)
    → ModelRouterError: No suitable models found for the given criteria

route(model_id="paid-b", free_only=True)
  → WARNING refusing specific model 'paid-b': not free-eligible under free_only
  → vrátí free-a

bez free_only (default) → free-a
```

**Pozor na to poslední:** volající explicitně žádal `paid-b` a **dostane
`free-a`**. To je fail-closed (placeného nedostane), ale je to **tiché
přesměrování**, o kterém volající nemusí vědět. Nález k otevření — viz §7.

### 4.3 (c) Syntetická citlivá data neuniknou

Payload s kartou, e-mailem a IBAN:

```
log obsahuje číslo karty?          False
log obsahuje e-mail?               False
EgressRequest obsahuje secret?     False
digest odpovídá SHA-256(payload)?  True
log obsahuje jen digest?           True

log: Authorizing model call: agent=agent-1, provider=ollama, model=free-a,
     payload_hash=b6599c1178c33705…, classifications=frozenset({'personal'})
```

Reálný `EgressGate`, neznámý provider:
`SecurityError: egress denied: provider is in neither the remote nor the local
allowlist`
Bez gate: `SecurityError: egress gate not configured; refusing to fail open`

### 4.4 Mutační kontrola — `mutation_check_f4.py`

Záměrně rozbíjeno, spuštěna suite, obnoveno. **10/10 PADÁ:**

| # | mutace | výsledek |
|---|---|---|
| M1 | `egress.authorize()` odstraněna | **5 failed** |
| M2 | fail-closed bez gate odstraněn | **2 failed** |
| M3 | filtr `free_only` v kandidátech | **4 failed** |
| M4 | filtr `free_only` ve fallbacku | **4 failed** |
| M5 | free check v `_route_specific_model` | **1 failed** |
| M6 | default `free_only=True` | **1 failed** |
| M7 | guard „no free model" odstraněn | **1 failed** |
| M8 | validation gate odstraněn | **1 failed** |
| M9 | `_is_free_eligible` vždy True | **4 failed** |
| M10 | filtr free backupu odstraněn | **3 failed** |

**Žádný test není slepý.** Repo po každé mutaci obnoveno, `40 passed`.

---

## 5. `mutation_check_f4.py` — NEODSTRANIT MLČKY

Je v **kořeni repozitáře**, není součástí žádné testovací trasy, `pytest` ho
nesbírá. Je to pracovní nástroj, ne test.

**Tři možnosti, potřebuješ se rozhodnout:**
1. přesunout do `scripts/` a nechat jako nástroj
2. přepsat jako parametrizovaný pytest (`pytest --f4-mutation`) → součást trasy
3. odstranit po Gate 3

Argument pro **2**: pravidlo „test, který umí umřít, je jediný důkaz, že test
něco chrání" se ztrácí, jakmile se nástroj odloží. Ale 10× prodlouží čas buildu,
takže to není jednoznačné.

Do té doby **nesmazat** — Gate 3 ho potřebuje jako opakovatelný důkaz.

---

## 6. Gate 3 — SELHALO, `REJECT_UNVERIFIED`

`security-auditor` vrátil verdikt, ale **nesplnil zadání**:

| Zadání | Skutečnost |
|---|---|
| 10 bodů mutační kontroly | **0 provedeno** |
| „ověř nebo vyvrát" u tvrzení 1–4 | dvě položky prohlásil `[UNVERIFIED]` místo ověření |
| `file:line` | částečně |

**Jeho tři nálezy ověřeny orchestrátorem, dva z nich jsou chybné:**

- `router.py:469` `_route_round_robin` "nemá `free_only`" — **VYVRÁCENO**,
  `file:line` nepravdivé
- `router.py:418` `_route_local_first` "nemá `free_only`" — **VYVRÁCENO**
- `model_egress.py:72` `destination=""` — **bezpečné**, viz §7.2

Podle vlastního slovníku odmítnutí je verdikt **`REJECT_UNVERIFIED`**: agent
prohlásil dvě věci za neověřené, místo aby je ověřil, a jeho hlavní kontrolu
neudělal vůbec.

**Proč selhal:** `security-auditor` má `bash: deny` (`orchestrator.md`). Nemohl
spustit mutaci ani test. To není jeho chyba — **zadání bylo nepoveditelné**.

**Jak udělat Gate 3 proveditelnou:**
- `tester` má `bash: allow` → mutační kontrola jemu
- `security-auditor` / `threat-modeler` dostanou **výstup** z mutace, ne
  zadání „proveď mutaci"
- nedávat jim úsehy, které vyžadují spuštění kódu

K tomu viz §7.1 — bez `bash` nemůže žádný auditní agent vydat platný Gate 3
verdikt na bezpečnostní kód.

---

## 7. Otevřené nálezy

### 7.1 Struktura: auditní agenti nemohou ověřovat bez `bash`

`security-auditor`, `threat-modeler`, `code-reviewer`, `verifier` mají
`bash: deny` záměrně (`tests/test_kilo_agent_policy.py:33` —
`MUST_BE_EXPLICIT_DENY`). **Důsledek:** Gate 3 na bezpečnostní změnu
je v této konfiguraci nedokončitelný.

To je buď chyba konfigurace, nebo chyba očekávání. `ROADMAP_V6.md` Gate 3
požaduje nezávislé posouzení — a posouzení bez možnosti spustit testy je
posouzení z čtení kódu, ne z prověření chování.

**Doporučení (vyžaduje schválení + změnu testu):** dát `security-auditor`
`bash: allow`. Neznámý vedlejší účinek: může spustit cokoliv, co chce
spustit. Alternativa: zůstat u `deny` a Gate 3 definovat jako
**kombinovaný** verdikt `tester` (běh) + auditor (čtení), oba s `file:line`.

### 7.2 `destination=""` v `EgressRequest` — bezpečné dnes, potenciálně nebezpečné

`model_egress.py:72` posílá prázdný destination. Dnes je to bezpečné:
`EgressGate._sanitize_destination()` (`egress.py:196`) vrací `""` pro prázdný
vstup a `_is_loopback_destination("")` je `False`.

**Ale:** `EgressRequest` si **nestaví destination sám**. Pokud provider někdy
přejde do `allowed_remote_providers`, prázdný destination projde větví bez
host kontroly. Doporučení: buď `destination` povinný parametr, nebo
`_validate_request()` (která dnes kontroluje jen `agent_id`, `provider_id` a
`payload_sha256` — `egress.py:242`) nechá prázdný destination selhat.

### 7.3 Registrace pluginu v `.kilo/kilo.jsonc` — stále neuděláno

`.kilo/plugin/cyberbrain26-freeauto/` existuje, je zkompilovaný, má 6 testů
v `tests/test_kilo_plugin_integration.py`. **V `.kilo/kilo.jsonc` není žádná
registrace** → „objekt, který se vytvoří a nikdy nezavolá, je totéž jako objekt,
který neexistuje" (`.kilo/agent/scout.md:61-67`).

Zápis do `.kilo/kilo.jsonc` vyžaduje schválení vlastníka. **Neprovádět bez něj.**

### 7.4 `KiloConfigBuilder` ani `RoleAssignmentStore` nejsou zapojeny

`grep -rn "RoleAssignmentStore\|KiloConfigBuilder" src/ tests/ --include=*.py`
→ **pouze jejich vlastní soubory a testy F4**. `ModelEgressGate` je zapojený
(§2.3), ale tyhle dvě ne. Jsou to **knihovní třídy bez volajícího**, ne mrtvý
kód, ale ani živá cesta. F4 gate (a) byl doložen přes `RoleAssignmentStore`
přímo ve skriptu, ne přes aplikační cestu.

### 7.5 `threading.Lock` v `role_assignment.py` se nepoužívá

`__init__` vytvoří `self._lock`, ale **žádná metoda ho nedrží**. Mrtvý kód nebo
nedokončená práce. Nízká závažnost, ale v bezpečnostní třídě je to
nečistota, která dovolí čtenému čtenému později chybě.

---

## 8. Co se ROZHODLO a NESMÍ se to vrátit

- **`external_directory` už není `deny` s `pattern: *`** (vlastník, 5. 10.).
  Regresní cesta: `.kilo/kilo.jsonc.bak-extdir`.
- **`bash: allow` mají jen `orchestrator` a `tester`** — nezměněno.
- **`implementer` selhal 3× po sobě na syntaxi**, vždy stejný vzorec: má
  `bash: deny`, takže si nemůže ověřit, že jeho kód importuje. Předchozí vlákno
  měřilo tentýž vzorec u `documenter` (zničení `orchestrator.md`, `verifier.md`,
  `implementer.md`). **Doporučení pro další vlákna: buď `implementer` povolit
  `py_compile`, nebo u bezpečnostně citlivého kódu opravuje syntax
  orchestrátor a deleguje se pouze review.**
- **`kilo` CLI opraveno** vlastníkem: `rm -f /usr/local/bin/kilo &&
  npm install -g @kilocode/cli@7.8.3` → `kilo --version` = `7.8.3`.
  Symlink dřív ukazoval na **neexistující** `/home/oem/.local/bin/kilo`;
  `mv` symlinku by nepomohl.
- **Cron review detektor** — vyřešení zůstává neověřené (`external_directory`
  teď propouští jen `/tmp/kilo`, ne `~/.config/kilo/`).

---

## 9. Delegace selhala — co z toho

Tři běhy `implementer` v tomto vlákně:

| běh | zadání | výsledek |
|---|---|---|
| 1 | opravit 4 soubory + napsat testy | 0 testů, 3 z 4 souborů stále neimportovatelné, tvrzené „successfully made changes" |
| 2 | jen `role_assignment.py` | opravil 3 ze 7 vad |
| 3 | jen `model_egress.py` | opravil 1 ze 4 vad |

**Žádný z nich nehlásil odchylku.** Handoff 21:50 §5 totéž u jiného agenta.
Můstek obou případů: **agent bez `bash` neověří vlastní zápis a doplní si to do
reportu.** Když zadání obsahuje syntaktickou chybu, agent bez `bash` nemá šanci
ji najít. To je důvod, proč se opravovalo ručně.

Výjimka potvrzuje pravidlo: běh, kde měl implementer napsat **testy** (bez
importu cizích modulů s chybnou cestou), selhal 29/31 kvůli importům na
neexistující moduly — ale soubor vytvořil. Když brief obsahuje změřené hodnoty
a chyba je v cíli, ne v syntaxi, funguje.

---

## 10. Co udělat v novém vlákně

1. **Gate 3** — viz §6. Zadání pro `tester` (mutace) + verdikt pro
   `security-auditor` (čtení). Nevydávat jim „proveď mutaci" bez `bash`.
2. **Rozhodnout o `mutation_check_f4.py`** (§5) — potřebuje vlastníka.
3. **Rozhodnout o `bash` pro auditní agenty** (§7.1) — mění bezpečnostní postoj,
   vyžaduje změnu `tests/test_kilo_agent_policy.py`.
4. **`destination=""`** (§7.2) — je to bezpečnostní nález v
   `execution_plane`, mimo F4 scope. Zapsat do F3 nebo opravit teď.
5. **Registrace pluginu** (§7.3) — připravit diff, **neaplikovat** bez schválení.
6. **`git status` před každým během** — pracovní strom není čistý a `1f80b29`
   je jediný bod obnavy.
7. **Nespouštět Gate 1 znovu bez důvodu** — zelená.

---

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

**Gate 3 v repu provedeno nebylo, ani jednou za existenci projektu.** Všechny
bezpečnostní tvrzení v tomto handoffu jsou `[OVĚŘENO]` ve smyslu **výstupu
příkazu**, což je jiná věc než nezávislé posouzení. Zvlášť §4.2 — tvrzení
„free_only nefunguje" **bylo odhaleno až během**, ne čtením kódu, a první
pokus Gate 3 to prohlásil za bezpředmětné.

Druhé slepé místo: mutační kontrola pokrývá **10 kontrol, které jsem si sám
vybral**. Neznamená to, že neexistuje 11. kontrola, kterou by šlo rozbít
nepozorovaně.

Třetí: `security-auditor` prohlásil dvě věci `[UNVERIFIED]` a jeho verdikt byl
`FAIL` s falečnými nálezy. **Souhlas agentů není důkaz a jejich nález není
automaticky pravdivý** — arXiv:2510.11822 (True Negative Rate < 25 %) a
arXiv:2606.19544 (exact-match agreement systematicky nadhodnocuje schopnost
rozhodovat). Viz `HANDOFF_2026-10-04-2150.md` §11.