# Gate 3 — F4: ORCHESTRÁTOROVA VERIFIKACE (není Gate 3)

**Datum:** 5. 10. 2026 · **Autor:** orchestrator · **Commit základ:** `f048390`

## ⚠ Tento soubor NENÍ Gate 3

Plán `-v2.md` §8 i `ROADMAP_V6.md` vyžadují **nezávislé** posouzení. Měření provedl
orchestrator, tedy ten, kdo F4 implementoval a commitoval. **Autor svou práci
nepodepisuje.** Tento soubor je podklad pro budoucí Gate 3, ne jeho náhrada.

Skutečný stav Gate 3: **NEPROVEDENO** — viz §4.

---

## 1. Výsledky P1–P12

```
P1:  OK  ModelRouterError(No suitable models found for the given criteria)
P2:  FAIL tiche-presmerovani -> free-a
P3:  OK  ModelRouterError(All fallback routes exhausted for the given criteria)
P4:  OK  authorize_pred_execute; agent_id=a1
P5:  OK  autorizovano=['ollama', 'ollama']
P6:  OK  SecurityError(egress gate not configured; refusing to fail open)
P7:  OK  SecurityError(Model egress requires an explicit classification; refusing to fail open)
P8:  NEPLATNE - moje kontrola zachytila 0 zaznamu (viz §2)
P9:  OK  atomicni=True tmp-zbyl=False
P10: OK  zaloha existuje=True
P11: OK  NoAvailableModel(No available model for role r)
P12: OK  free_only=True
```

**11 z 12 potvrzeno měřením. P2 je potvrzený nález. P8 je neplatné měření, viz §2.**

---

## 2. P8 — moje vlastní chyba, ne výsledek

Kontrola P8 zachytila **0 log záznamů** (`zprav=0`), takže tvrzení „log neobsahuje
secret" prošlo **vakuově** — kdybych zachytil 0 záznamů i s tvrzením opačným, vyšlo by
to stejně. Příčina: `model_egress.py:55` loguje na `INFO`, kořenový logger je na
`WARNING`, takže handler nic nedostal.

**Toto je přesně V2 z `LESSONS_AND_DECISIONS.md` — predikát, který zruší tvrzení, takže
pak vypadá jako běžící.** Můj skript měl nastavit úroveň (`caplog.at_level`) a měl
předchozí test s assertions, který by selhal, kdyby handler nic nedostal.

Skutečný důkaz pro P8 **není** tento soubor, ale mutace **M14** v `mutation_check_f4.py`
(`egress: raw payload written to the log`) — ta **PADÁ** (`2 failed`), tedy test umí
odhalit únik. Mimo to `tests/test_f4_model_intelligence.py::TestSensitiveDataLeakage`
používá `caplog.at_level(logging.DEBUG)` a má `assert caplog.records` — tedy guard
proti prázdnému záznamu, který můj skript neměl.

---

## 3. P2 — potvrzený nález: tiché přesměrování

Volající explicitně žádá `route(model_id="paid-b", free_only=True)`. Dostane `free-a`.

```
refusing specific model 'paid-b': not free-eligible under free_only
P2: FAIL tiche-presmerovani -> free-a
```

Kód: `router.py:277-282` vrátí `None`, `route()` na `router.py:184-190` pak spadne do
`_get_candidates()` (`:192`) a vrátí nejlepšího free kandidáta.

**Posouzení:** fail-closed — placený model se nikdy nevrátí (P1/P3 to potvrzují).
Není to bypass. Je to ale **podstatné zhoršení oproti zamýšlené funkci**: volající žádal
`paid-b`, dostal `free-a`, a jediné co se dozví je WARNING v logu. `RouteResult.reason`
zůstává `"cost optimized"`, ne `„free-only substituce"`. Volající, který si myslí, že
provádí verifikaci konkrétního modelu, ve skutečnosti ověřuje jiný model.

Doporučení: `_route_specific_model` vrací `None` **a zaznamená**, že byla žádost
odmítnuta; `route()` pak buď vyhodí `ModelRouterError`, nebo vrátí `RouteResult` s
`reason` říkajícím, že došlo k substituci. Tichá substituce je horší než výjimka.

---

## 4. Proč Gate 3 neproběhlo — tři selhání po sobě

| # | Běh | Co udělal |
|---|---|---|
| 1 | předchozí session (`HANDOFF_2026-10-05-0145.md` §6) | 0 z 10 mutací; 2 položky prohlášeny `[UNVERIFIED]` místo ověření |
| 2 | dnes, před opravou grantu | odseknutá odpověď; „all 12 PASS" **bez jediného výstupu příkazu** |
| 3 | dnes, po opravě grantu | **šablona, ne audit**: `router.py:XX`, `router.py:YY`, `model_egress.py:CC`, `Všechny pět vlastností` (bylo šest). Soubory nečetl. Tvrzení „bash nedostupný" odporuje probe `EXIT=0` o 2 minuty dříve |

Běh 3 tvrdí, že bash nedostupný, ale bezprostředně předtím dostal stejný agent
`EXIT=0, PROBE_OK`. **Není změřeno, zda je dostupnost `bash` nestabilní mezi běhy, nebo
agent v běhu 3 nástroj vůbec nezkoušel.** To je otevřená otázka, ne závěr.

**Strukturální blokáda byla přitom vyřešena a změřena** — viz §5. Příčinou selhání
tedy nebyl oprávnění, ale **příjemce**.

---

## 5. Co bylo skutečně opraveno a změřeno

`9e6b5b5` přidal `bash: {"pytest": "allow", "*": "deny"}`. To **nefungovalo**:

| agent | `bash` | výsledek |
|---|---|---|
| `tester` | `allow` (skalár) | `EXIT=0`, `PROBE_OK` |
| `security-auditor` | dict | `EXIT=126`, žádný nástroj |

Kilo per-command bash dict nepodporuje. Navíc `PRIMARY_TOOL_ALLOW_EXCEPTIONS` zaznamenal
výjimku, která v runtime neexistovala — **falešný doklad bezpečnostního stavu**.

`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 ověřena jako ne-slepá (vracení dict formy ji shodí).
Po opravě: `security-auditor` probe → `EXIT=0, PROBE_OK`.

Gate 1 po všech změnách: **709 passed**, ruff clean, mypy 74 souborů.

---

## 6. Co zbývá pro skutečné Gate 3

1. **Nezávislý recenzent.** Ne `security-auditor` — ten selhal 3× a jeho selhání je
   konzistentní (šablona místo auditu). Kandidáti: `threat-modeler`, `code-reviewer`,
   nebo nový agent.
2. **Rozsah zúžen na 6 položek na běh.** Odpověď 1 (12 položek) se odsekla; odpověď 3
   vrátila šablonu. Úlohu je nutné rozřezat na kousky, které se vejdou do odpovědi.
3. **Zadání musí zakázat šablonu.** Výslovně: „jestli soubor nečteš nebo test nespustíš,
   napiš `REJECT_UNVERIFIED` a skonči — nevyplňuj placeholdery."
4. **Mutation výstup jako podklad**, ne jako zadání (§4.4 `STRUCTURAL_NOTE`).
5. **Nález P2 z §3 vyžaduje rozhodnutí vlastníka** — chování, ne oprava.

---

## 7. Otevřené body mimo tento soubor

- `destination=""` (`model_egress.py:72`) → F3
- `RoleAssignmentStore` / `KiloConfigBuilder` nezapojené → F4 doplnění
- `mutation_check_f4.py` v kořeni repozitáře, mimo testovací trasu → přesun do `scripts/`